3

我正在尝试在我当前的 Servicestack 实现中支持 MessagePack 协议。我需要它来支持(反)序列化这样定义的 ISegment 列表:

[KnownType(typeof(ArcSegment)), KnownType(typeof(LineSegment))]
public class PathRequest
{
  public List<ISegment> Segments {get;set;}
}

public interface ISegment
{
  Point StartPoint {get;set;}
  Point EndPoint {get;set;}
}

public class ArcSegment: ISegment {...}
public class LineSegment: ISegment {...}

开箱即用它告诉我

类型“Asi.Geometry.ISegment”没有默认(无参数)公共构造函数和具有 Int32 参数的公共构造函数。

这是非常真实的。显然它不使用 KnownType 属性。在网上阅读后,我发现我可以制作自己的序列化器。因此我尝试了这个:

class ArcLineSerializer: MessagePackSerializer<ISegment>
{
    private readonly MessagePackSerializer<ArcSegment> _arcSerializer = MessagePackSerializer.Create<ArcSegment>();
    private readonly MessagePackSerializer<LineSegment> _lineSerializer = MessagePackSerializer.Create<LineSegment>();

    protected override void PackToCore(Packer packer, ISegment objectTree)
    {
        if(objectTree is ArcSegment)
            _arcSerializer.PackTo(packer, (ArcSegment)objectTree);
        else if (objectTree is LineSegment)
            _lineSerializer.PackTo(packer, (LineSegment)objectTree);
        else
            throw new NotSupportedException();
    }

    protected override ISegment UnpackFromCore(Unpacker unpacker)
    {
        var data = unpacker.Data;
        if (data != null)
        {
            if (data.Value.IsTypeOf<ArcSegment>().GetValueOrDefault())
                return _arcSerializer.UnpackFrom(unpacker);
            if (data.Value.IsTypeOf<LineSegment>().GetValueOrDefault())
                return _lineSerializer.UnpackFrom(unpacker);
            throw new NotSupportedException();
        }
        return null;
    }
}

唉,这给了我尝试构造 _arcSerializer 的相同错误。它是怎么做的?

4

1 回答 1

2

您不能只从消息中获取打包类的类型,而是可以将一些类型标识符与序列化数据一起传递。

这就是接口序列化的通用方式。

class InterfaceSerializer<T> : MessagePackSerializer<T>
{
    private Dictionary<string, IMessagePackSerializer> _serializers;

    public InterfaceSerializer()
        : this(SerializationContext.Default)
    {
    }

    public InterfaceSerializer(SerializationContext context)
    {
        _serializers = new Dictionary<string, IMessagePackSerializer>();

        // Get all types that implement T interface
        var implementingTypes = System.Reflection.Assembly
            .GetExecutingAssembly()
            .DefinedTypes
            .Where(t => t.ImplementedInterfaces.Contains(typeof(T)));

        // Create serializer for each type and store it in dictionary
        foreach (var type in implementingTypes)
        {
            var key = type.Name;
            var value = MessagePackSerializer.Create(type, context);
            _serializers.Add(key, value);
        }
    }

    protected override void PackToCore(Packer packer, T objectTree)
    {
        IMessagePackSerializer serializer;
        string typeName = objectTree.GetType().Name;

        // Find matching serializer
        if (!_serializers.TryGetValue(typeName, out serializer))
        {
            throw SerializationExceptions.NewTypeCannotSerialize(typeof(T));
        }

        packer.PackArrayHeader(2);             // Two-element array:
        packer.PackString(typeName);           //  0: Type name
        serializer.PackTo(packer, objectTree); //  1: Packed object
    }

    protected override T UnpackFromCore(Unpacker unpacker)
    {
        IMessagePackSerializer serializer;
        string typeName;

        // Read type name and packed object
        if (!(unpacker.ReadString(out typeName) && unpacker.Read()))
        {
            throw SerializationExceptions.NewUnexpectedEndOfStream();
        }

        // Find matching serializer
        if (!_serializers.TryGetValue(typeName, out serializer))
        {
            throw SerializationExceptions.NewTypeCannotDeserialize(typeof(T));
        }

        // Unpack and return
        return (T)serializer.UnpackFrom(unpacker);
    }
}

您需要为所需的接口注册自定义序列化程序。例如,使用默认序列化上下文,您可以:

SerializationContext.Default.Serializers
    .Register<ISegment>(new InterfaceSerializer<ISegment>());

由于注册了序列化程序,因此可以像往常一样打包和解包包含 ISegment 的对象。

var packer = MessagePackSerializer.Create<PathRequest>();
packer.Pack(stream, somePathRequest);
stream.Position = 0;
var unpackedPathRequest = packer.Unpack(stream);
于 2013-04-14T00:09:02.843 回答