Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem With Registering Polymorphic Objects #301

Open
AveryBoyd opened this issue Sep 4, 2018 · 0 comments
Open

Problem With Registering Polymorphic Objects #301

AveryBoyd opened this issue Sep 4, 2018 · 0 comments

Comments

@AveryBoyd
Copy link

AveryBoyd commented Sep 4, 2018

Registering polymorphic objects directly does not work correctly when trying to serialize those objects from other objects.

Here is an example:

public class Wrapper
{
    public Letters field { get; set; }
}

public interface Letters
{

}

public class A : Letters
{
    public string ItemA { get; set; }
}

public class B : Letters
{
    public string ItemB { get; set; }
}

public abstract class SubType
{
}

public class SubTypeA : SubType
{
    public string ItemD { get; set; }
}

public class SubTypeB : SubType
{
    public string ItemE { get; set; }
}

As shown, the polymorphic 'Letters' is within the class 'Wrapper'.
Now trying to register these to the SerializationContext...

        SerializationContext context = new SerializationContext();

        context.GetSerializer<SubType>(
            PolymorphismSchema.ForPolymorphicObject(
                typeof(SubType),
                new Dictionary<string, Type>
                {
                    {"A", typeof(SubTypeA)},
                    {"B", typeof(SubTypeB)}
                }));

        context.GetSerializer<IType>(
            PolymorphismSchema.ForPolymorphicObject(
                typeof(IType),
                new Dictionary<string, Type>
                {
                    {"A", typeof(TypeA)},
                    {"B", typeof(TypeB)}
                }));

        MessagePackSerializer<Wrapper> serializerWrapper = context.GetSerializer<Wrapper>();
        // The above line breaks due to 'System.NotSupportedException: 'This operation is not supported because 'IType' cannot be instantiated.'

`

This shouldn't happen, because the context should internally register the serializers it gets.

From what I've dug up, the problem is that the serializer that context.GetSerializer creates is null for polymorphic types.

In SerializationContext.cs line 692:
serializer = this.OnResolveSerializer<T>( schema ) ?? MessagePackSerializer.CreateInternal<T>( this, schema );
MessagePackSerialier.CreateInternal is called.
And then in MessagePackSerializer.Factories.cs lines 247-251:

if ( concreteType == null )
{
    // return null for polymoirphic provider.
    return null;
}

The returned serializer is null, and so the provider is given a null default serializer at SerializationContext.cs line 724:
provider = new PolymorphicSerializerProvider<T>( serializer );
And then when SerializationContext.cs line 741 is reached:

this._serializers.Register(
    typeof( T ),
    provider,
    nullableType,
    nullableSerializerProvider,
    SerializerRegistrationOptions.WithNullable
)

A faulty serializer is registered.

But, context.GetSerializer returns the correct serializer because SerializationContext.cs line 760
return this._serializers.Get<T>( this, providerParameter );
will use the schema to create the correct serializer.

A work around for this issue is:

   context.Serializers.RegisterOverride(
        context.GetSerializer<IType>(
            PolymorphismSchema.ForPolymorphicObject(
                typeof(IType),
                new Dictionary<string, Type>
                {
                    {"A", typeof(TypeA)},
                    {"B", typeof(TypeB)}
                })));

Where the correctly returned serializer is then re-registered with the context.

But, I propose solving this issue by changing the MessagePackSerializer.Factories.cs lines 247-251 to this:

if ( concreteType == null )
{
    if ( schema == null )
    {
	    return null; // maybe do something else here?
    }
    if ( schema.UseTypeEmbedding )
    {
	    return new TypeEmbedingPolymorphicMessagePackSerializer<T>( context, schema );
    }
    else
    {
	    return new KnownTypePolymorphicMessagePackSerializer<T>( context, schema );
    }
}

This is what PolymorphicSerializerProvider.cs does anyway at the end of context.GetSerializer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant