dotnet-avro icon indicating copy to clipboard operation
dotnet-avro copied to clipboard

Support `IsRequired` attribute

Open mdrgeniebelt opened this issue 4 years ago • 1 comments

Hi, me again with another question.

Is it possible to enforce nullability in schema of reference types?

This is our contract in code:

[DataContract(Name = "accountEvent")]
public class AccountEvent
{
    [DataMember(Name = "id")]
    public Guid Id { get; set; }
    
    [DataMember(Name = "name")]
    public string Name { get; set; }
    
    [DataMember(Name = "email")]
    public string Email { get; set; }
    
    [DataMember(Name = "confirmed")]
    public bool Confirmed { get; set; }

    [DataMember(Name = "phoneNumber", IsRequired = false)]
    public string? PhoneNumber { get; set; } = null;
}

Schema registered in registry:

{
    "type": "record",
    "name": "accountEvent",
    "fields": [
        {
            "name": "confirmed",
            "type": "boolean"
        },
        {
            "name": "email",
            "type": "string"
        },
        {
            "name": "id",
            "type": {
                "type": "string",
                "logicalType": "uuid"
            }
        },
        {
            "name": "name",
            "type": "string"
        },
        {
            "name": "phoneNumber",
            "type": "string"
        }
    ]
}

I was trying to use IsRequired but it didn't change anything (as expected after reading its documentation ;)).

What we would like to achieve is:

{
    "name": "phoneNumber",
    "type": [ "string", "null" ]
}

I'd like to help with implementing it - if you want help of course. Just let me know if that is possible or not because you'll probably know :)

mdrgeniebelt avatar Feb 12 '21 10:02 mdrgeniebelt

We’re hoping to ship support for nullable reference types with 8.0.0 soon (see https://github.com/ch-robinson/dotnet-avro/issues/102). Currently, it’s all-or-nothing—you can set the resolver to follow .NET’s nullable semantics...

using Chr.Avro.Abstract;
using Chr.Avro.Confluent;
using Chr.Avro.Resolution;
using Confluent.Kafka;
using Confluent.SchemaRegistry;

var registry = new CachedSchemaRegistryClient(new SchemaRegistryConfig { ... });

var typeResolver = new ReflectionResolver(
  resolveReferenceTypesAsNullable: true  // schema builder will produce ["null", ...] for all reference types
);

var schemaBuilder = new SchemaBuilder(typeResolver: typeResolver);
var serializerBuilder = new SchemaRegistrySerializerBuilder(registry, schemaBuilder: schemaBuilder)
var producerBuilder = new ProducerBuilder<Ignore, SomeValue>();

await builder.SetAvroValueSerializer(serializerBuilder, "test-subject", registerAutomatically: AutomaticRegistrationBehavior.Always);

... but there isn’t a way to pick and choose fields short of modifying the abstract schema.

It’d be nice to support [IsRequired] as well. I’m leery of possible contradictions with nullable types (e.g. https://stackoverflow.com/a/7767083), but I think they can both be implemented with an IsNullable property on FieldResolution.

dstelljes avatar Feb 12 '21 14:02 dstelljes