Kenneth Ry Ulrik created AVRO-4318:
--------------------------------------
Summary: C# AvroGen: Marker Interface for Avro Unions
Key: AVRO-4318
URL: https://issues.apache.org/jira/browse/AVRO-4318
Project: Apache Avro
Issue Type: Improvement
Components: csharp
Affects Versions: 1.12.1
Reporter: Kenneth Ry Ulrik
Currently, if a field in an Avro record defines an Avro Union of types, the
resulting C# class property type becomes "object", leaving
*Sample schema:*
{code:java}
{
"name": "RecordWithAvroUnionField",
"namespace": "example",
"type": "record",
"fields": [
{
"name": "avroUnionField",
"type": [
{
"name": "AvroUnionTypeA",
"type": "record",
"fields": [
{
"name": "myString",
"type": "string"
}
]
},
{
"name": "AvroUnionTypeB",
"type": "record",
"fields": [
{
"name": "myInt",
"type": "int"
}
]
}
]
}
]
}{code}
*What gets generated today:*
* RecordWithAvroUnionField.cs
* AvroUnionTypeA.cs
* AvroUnionTypeB.cs
+What's wrong with it?+
* The "avroUnionField" field is generated as a property with type "object"
* Despite RecordWithAvroUnionField literally containing and declaring the
types AvroUnionTypeA and AvroUnionTypeB, there is no type system reference,
direct or otherwise, to them at all, so it's up to consumers to "guess" /
explicitly cast into types that they must in advance know can be in this
"object"-typed property.
+Excerpt of generated "avroUnionField" C# property:+
(generates "object" property, requiring manual "guessing" / manual type-casting
to explicitly known types )
{code:java}
public object avroUnionField
{
get
{
return this._avroUnionField;
}
set
{
this._avroUnionField = value;
}
}{code}
+Excerpt of generated "AvroUnionTypeA" type as an example:+
(has no type system correlation to RecordWithAvroUnionField in any way)
{code:java}
public partial class AvroUnionTypeA : global::Avro.Specific.ISpecificRecord
{ ... } {code}
*The proposal for what to generate instead:*
* A marker (empty) interface, distinctly defined and named according to the
type and field name
* For each type declared as part of an Avro union, add the marker interface
onto the generated class.
* Modify Avro Union fields to use their distinctly defined marker interface as
the property type instead of object
+Marker interface naming proposal:+
(ensures uniqueness, avoiding rare scenarios where multiple nested types
declared in Avro Unions may declare a field with the same name, both of which
declare an Avro Union)
Format: $"\{NameOfTypeContainingAvroUnionField}_\{AvroUnionFieldName}"
{code:java}
public interface IRecordWithAvroUnionField_avroUnionField { }{code}
+"AvroUnionTypeA" type example:+
(implements the marker interface, because it is declared as part of this
particular Avro Union)
{code:java}
public partial class AvroUnionTypeA : global::Avro.Specific.ISpecificRecord,
IRecordWithAvroUnionField_avroUnionField
{ ... } {code}
+"avroUnionField" C# property in the "RecordWithAvroUnionField" class:+
(uses the marker interface as the type of the Avro Union field)
{code:java}
public IRecordWithAvroUnionField_avroUnionField avroUnionField
{
get
{
return this._avroUnionField;
}
set
{
this._avroUnionField = value;
}
}{code}
*Future note for C# union types:*
With C# 15 and .NET 11, with the introduction of union types, one could instead
generate a union type definition, with each class generated for the Avro Union
types being declared as part of that union type, allowing the C# compiler to
further aid developers by ensuring complete handling of all union member-type
possibilities in code that deserializes to the generated class.
[Explore union types in C# 15 - .NET
Blog|https://devblogs.microsoft.com/dotnet/csharp-15-union-types/]
I.e.:
{code:java}
public union RecordWithAvroUnionField_avroUnionField(AvroUnionTypeA,
AvroUnionTypeB); {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)