Null handling and NullableSchema

May 28, 2014 at 7:46 PM
When using on-the-fly schema creation, by default all types that do not have NullableSchema will crash (and not very nicely when the object is a child of the object being deserialized).

There is already a property IsRequired on DataMember, which achieves the same thing; also, using NullableSchemaAttribute requires referencing Microsoft.Avro.dll and Newtonsoft.Json.dll in multiple Model assemblies that would otherwise not need it.

I suggest (and can provide a pull request) using DataMemberAttribute.IsRequired, though this would break existing code (well, not break it but introduce Nullable fields where not needed). To that effect, maybe there could be a setting in AvroSerializer, e.g. AvroSerializerSettings.AlwaysNullable.

Let me know your throughts!

For people who search for keywords like I did, here is the errors I get:
System.Runtime.Serialization.SerializationException : Unexpected null value for the object of type 'MyType'. Please check the schema.
and this for children objects, without other details:
System.ArgumentNullException : Value cannot be null.
Parameter name: value
Jul 14, 2014 at 9:12 PM
Edited Jul 14, 2014 at 9:13 PM
100% agreed

i've just faced the same problem
the standard DataMemberAttribute.IsRequired should be used instead of NullableSchemaAttribute
Jul 15, 2014 at 2:10 PM
This would be a must.

The workaround - when workaround, there is - is just too painful / costly. A deal-breaker for Avro right now.
Jul 17, 2014 at 11:31 AM
Hello!

There are two possible answers here.

Option 1.

The resolver constructor has a parameter (allowNullable), the purpose of this parameter is to determine whether we want the schema generated from a C# type to either:
  • (allowNullable = true) follow natural C# nullability of types (e.g. classes, nullable value types can be nulls), this means a schema generated from a class Foo is Avro union [null, Foo], or:
  • (allowNullable = false (default)) assume everything is not nullable (e.g. class Foo will be mapped to Avro record Foo) allowing the user to explicitly determine what can be null using the [NullableSchema] attribute.
Example:
Foo{
       int i;
       int? j;
       Bar b;
}
(allowNullables=true)
Foo{           => [null, Foo]
       int i;  => [int]
       int? j; => [null, int]
       Bar b;  => [null, bar]
}
(allowNullables=false)
Foo{           => [Foo]
       int i;  => [int]
       int? j; => [int]
       Bar b;  => [bar]
}
(allowNullables=false)
Foo{              => [Foo]
       [NullableSchema]
       int i;     => [null, int]
       [NullableSchema]
       int? j;    => [null, int]
       [NullableSchema]
       Bar b;     => [null, bar]
}
This effectively solved the issue with Nullables (see also this)

Option 2.

We also support nulls for data contract if the resolver is created like { Resolver = new AvroDataComtractResolver(true) }. Maybe this will be even better for the scenario in the title post.