将 MsgPack 与 Servicestack 结合使用:如何执行 KnownType?

问题描述 投票:0回答:2

我正在尝试在当前的 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 时遇到的同样的错误。做得怎么样?

c# serialization servicestack msgpack messagepack
2个回答
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);

0
投票

对于未来的读者,我相信您可以将

KnownType
属性添加到接口声明中。

在此示例中:

[KnownType(typeof(ArcSegment))]
[KnownType(typeof(LineSegment))]
public interface ISegment
{
  ...
}

无需编写整个自定义序列化器。

© www.soinside.com 2019 - 2024. All rights reserved.