dahomey-technologies/Dahomey.Cbor

Deserialization of abstract record class fails

rmja opened this issue · 3 comments

rmja commented

Consider this case:

public abstract record class Animal(DateTime Timestamp)
    {
    }

[CborDiscriminator("dog")]
    public record class Dog(string Color, DateTime Timestamp) : Ingress(Timestamp)
    {
        // This example fails if this empty constructor is not present
        private Dog() : this("black", default)
        {
        }
    }

public static class DataIngressCborOptions
    {
        public static readonly CborOptions Value = new();

        static DataIngressCborOptions()
        {
            Value.Registry.DiscriminatorConventionRegistry.RegisterConvention(new AttributeBasedDiscriminatorConvention<string>(Value.Registry));
            Value.Registry.DiscriminatorConventionRegistry.RegisterType(typeof(Dog));
        }
    }

Cbor.Deserialize<Animal[]>(..., DataIngressCborOptions.Value);

In the example, if the Dog ctor is not defined, _constructor is never assigned in ObjectConverter causing the exception A CreatorMapping should be defined for interfaces or abstract classes.

rmja commented

Ok. I will try and reproduce this and get back

rmja commented

@mcatanzariti The serialization/deserialization needs to be done on the abstract type. Here is a modified version of Issue0091.cs that fails:

#if NET6_0_OR_GREATER

using Dahomey.Cbor.Attributes;
using Dahomey.Cbor.Serialization.Conventions;
using System;
using Xunit;

namespace Dahomey.Cbor.Tests.Issues
{
    public class Issue0091
    {
        public abstract record class Animal(DateTime Timestamp)
        {
        }

        [CborDiscriminator("dog")]
        public record class Dog(string Color, DateTime Timestamp) : Animal(Timestamp)
        {
            // This example does not fail if this empty constructor is present
            //private Dog() : this("black", default)
            //{
            //}
        }

        [Fact]
        public void TestReadWrite()
        {
            CborOptions options = new CborOptions();
            options.Registry.DiscriminatorConventionRegistry.RegisterConvention(new AttributeBasedDiscriminatorConvention<string>(options.Registry));
            options.Registry.DiscriminatorConventionRegistry.RegisterType(typeof(Dog));

            Dog dog = new("black", DateTime.Parse("2022-10-24T14:05:08Z"));
            var cbor = Helper.Write<Animal>(dog, options);

            Animal deserialized = Helper.Read<Animal>(cbor, options);

            Assert.NotNull(deserialized);
            Dog dog2 = Assert.IsType<Dog>(deserialized);
            Assert.Equal(dog.Color, dog2.Color);
            Assert.Equal(dog.Timestamp, dog2.Timestamp);
        }
    }
}

#endif