From 6fc8773e9e06eda067f35e7fb4e29ebb2ad575c3 Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:31:43 +1100 Subject: [PATCH 1/5] Implement nullable type handling in union schemas Add null schema handling for union schemas in ReflectionSchemaBuilder. --- .../BuildSchema/ReflectionSchemaBuilder.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/AvroConvert/AvroObjectServices/BuildSchema/ReflectionSchemaBuilder.cs b/src/AvroConvert/AvroObjectServices/BuildSchema/ReflectionSchemaBuilder.cs index 783245e..bb27b4b 100644 --- a/src/AvroConvert/AvroObjectServices/BuildSchema/ReflectionSchemaBuilder.cs +++ b/src/AvroConvert/AvroObjectServices/BuildSchema/ReflectionSchemaBuilder.cs @@ -440,9 +440,17 @@ private TypeSchema TryBuildUnionSchema(Type memberType, MemberInfo memberInfo, D { result.Add(memberType); } + + var unionSchemas = result.Select(type => CreateNotNullableSchema(type, schemas, currentDepth + 1, memberInfo, representation)).ToList(); + var typeInfo = _resolver.ResolveType(memberType, memberInfo); - return new UnionSchema(result.Select(type => CreateNotNullableSchema(type, schemas, currentDepth + 1, memberInfo, representation)).ToList(), memberType); + if (typeInfo.Nullable) + { + unionSchemas.Insert(0, new NullSchema()); + } + + return new UnionSchema(unionSchemas, memberType); } private FixedSchema TryBuildFixedSchema(Type memberType, MemberInfo memberInfo, NamedSchema parentSchema) From 0ccd02353979fa4a25bb8dce09e956636afd2cf9 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 7 Mar 2026 21:51:11 +1100 Subject: [PATCH 2/5] fix enum parsing for unions --- .../AvroObjectServices/Read/Resolvers/Enum.cs | 25 +++++++++++++++++++ .../AnonymousAndExpandoTypes.cs | 23 ++++++++++++++++- tests/AvroConvertTests/TestClasses.cs | 10 +++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs b/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs index 387e8ec..6b5644f 100644 --- a/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs +++ b/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs @@ -16,6 +16,8 @@ #endregion using System; +using System.Linq; +using System.Reflection; using SolTechnology.Avro.AvroObjectServices.Schemas; using SolTechnology.Avro.AvroObjectServices.Schemas.Abstract; @@ -32,7 +34,30 @@ protected virtual object ResolveEnum(EnumSchema writerSchema, TypeSchema readerS int position = d.ReadEnum(); string value = writerSchema.Symbols[position]; + + if (type == typeof(object)) + { + var targetType = GetClrTypeForEnum(readerSchema, new[] { Assembly.GetExecutingAssembly(), Assembly.GetEntryAssembly() }); + + if (targetType == null) + { + targetType = GetClrTypeForEnum(readerSchema, AppDomain.CurrentDomain.GetAssemblies()); + } + + if (targetType != null) + { + type = targetType; + } + } + return EnumParser.Parse(type, value, _namingPolicy); } + + private Type GetClrTypeForEnum(TypeSchema schema, Assembly[] assemblies) + { + return assemblies + .Select(x => x.GetType(schema.Name)) + .FirstOrDefault(x => x != null); + } } } \ No newline at end of file diff --git a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs index 2e768e8..908852d 100644 --- a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs +++ b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Dynamic; using FluentAssertions; @@ -126,6 +126,27 @@ public void DeserializeByLine_CollectionWithUnionOfClasses_ResultIsTheSameAsInpu //Assert Assert.Equivalent(toSerialize, deserialized); } + + [Theory] + [MemberData(nameof(TestEngine.CoreUsingSchema), MemberType = typeof(TestEngine))] + public void Deserialize_AvroUnionsWithEnums_ResultGetsEnumType(Func engine) + { + //Arrange + var schema = AvroConvert.GenerateSchema(typeof(TypeWithUnionEnums)); + + var toSerialize = new TypeWithUnionAvro + { + TopLevelField = "First", + UnionField = TestEnum.be + }; + + //Act + var deserialized = (TypeWithUnionEnums)engine.Invoke(toSerialize, typeof(TypeWithUnionEnums), schema, schema); + + //Assert + Assert.Equivalent(toSerialize, deserialized); + Assert.IsType(deserialized.EnumField); + } } public class AnonymousLikeClass diff --git a/tests/AvroConvertTests/TestClasses.cs b/tests/AvroConvertTests/TestClasses.cs index f7e8d7e..136d990 100644 --- a/tests/AvroConvertTests/TestClasses.cs +++ b/tests/AvroConvertTests/TestClasses.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; @@ -517,6 +517,14 @@ public class TypeWithUnionAvro public object UnionField { get; set; } } + public class TypeWithUnionEnums + { + public string TopLevelField { get; set; } + + [AvroUnion(typeof(TestEnum), typeof(EnumWithDifferentNames))] + public object EnumField { get; set; } + } + public class ObjA { public string FieldA { get; set; } From 43d0eab7c5ab9675499f587c5235d04a01961f79 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 7 Mar 2026 22:00:50 +1100 Subject: [PATCH 3/5] fix test --- .../AnonymousAndExpandoTypes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs index 908852d..040fdfd 100644 --- a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs +++ b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs @@ -134,10 +134,10 @@ public void Deserialize_AvroUnionsWithEnums_ResultGetsEnumType(Func Date: Sun, 8 Mar 2026 08:22:43 +1100 Subject: [PATCH 4/5] Revert "fix test" This reverts commit 43d0eab7c5ab9675499f587c5235d04a01961f79. --- .../AnonymousAndExpandoTypes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs index 040fdfd..908852d 100644 --- a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs +++ b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs @@ -134,10 +134,10 @@ public void Deserialize_AvroUnionsWithEnums_ResultGetsEnumType(Func Date: Sun, 8 Mar 2026 08:22:50 +1100 Subject: [PATCH 5/5] Revert "fix enum parsing for unions" This reverts commit 0ccd02353979fa4a25bb8dce09e956636afd2cf9. --- .../AvroObjectServices/Read/Resolvers/Enum.cs | 25 ------------------- .../AnonymousAndExpandoTypes.cs | 23 +---------------- tests/AvroConvertTests/TestClasses.cs | 10 +------- 3 files changed, 2 insertions(+), 56 deletions(-) diff --git a/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs b/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs index 6b5644f..387e8ec 100644 --- a/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs +++ b/src/AvroConvert/AvroObjectServices/Read/Resolvers/Enum.cs @@ -16,8 +16,6 @@ #endregion using System; -using System.Linq; -using System.Reflection; using SolTechnology.Avro.AvroObjectServices.Schemas; using SolTechnology.Avro.AvroObjectServices.Schemas.Abstract; @@ -34,30 +32,7 @@ protected virtual object ResolveEnum(EnumSchema writerSchema, TypeSchema readerS int position = d.ReadEnum(); string value = writerSchema.Symbols[position]; - - if (type == typeof(object)) - { - var targetType = GetClrTypeForEnum(readerSchema, new[] { Assembly.GetExecutingAssembly(), Assembly.GetEntryAssembly() }); - - if (targetType == null) - { - targetType = GetClrTypeForEnum(readerSchema, AppDomain.CurrentDomain.GetAssemblies()); - } - - if (targetType != null) - { - type = targetType; - } - } - return EnumParser.Parse(type, value, _namingPolicy); } - - private Type GetClrTypeForEnum(TypeSchema schema, Assembly[] assemblies) - { - return assemblies - .Select(x => x.GetType(schema.Name)) - .FirstOrDefault(x => x != null); - } } } \ No newline at end of file diff --git a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs index 908852d..2e768e8 100644 --- a/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs +++ b/tests/AvroConvertTests/FullSerializationAndDeserialization/AnonymousAndExpandoTypes.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Dynamic; using FluentAssertions; @@ -126,27 +126,6 @@ public void DeserializeByLine_CollectionWithUnionOfClasses_ResultIsTheSameAsInpu //Assert Assert.Equivalent(toSerialize, deserialized); } - - [Theory] - [MemberData(nameof(TestEngine.CoreUsingSchema), MemberType = typeof(TestEngine))] - public void Deserialize_AvroUnionsWithEnums_ResultGetsEnumType(Func engine) - { - //Arrange - var schema = AvroConvert.GenerateSchema(typeof(TypeWithUnionEnums)); - - var toSerialize = new TypeWithUnionAvro - { - TopLevelField = "First", - UnionField = TestEnum.be - }; - - //Act - var deserialized = (TypeWithUnionEnums)engine.Invoke(toSerialize, typeof(TypeWithUnionEnums), schema, schema); - - //Assert - Assert.Equivalent(toSerialize, deserialized); - Assert.IsType(deserialized.EnumField); - } } public class AnonymousLikeClass diff --git a/tests/AvroConvertTests/TestClasses.cs b/tests/AvroConvertTests/TestClasses.cs index 136d990..f7e8d7e 100644 --- a/tests/AvroConvertTests/TestClasses.cs +++ b/tests/AvroConvertTests/TestClasses.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; @@ -517,14 +517,6 @@ public class TypeWithUnionAvro public object UnionField { get; set; } } - public class TypeWithUnionEnums - { - public string TopLevelField { get; set; } - - [AvroUnion(typeof(TestEnum), typeof(EnumWithDifferentNames))] - public object EnumField { get; set; } - } - public class ObjA { public string FieldA { get; set; }