使用MsgPack和Servicestack:我如何做KnownType?

时间:2013-01-18 22:10:38

标签: c# serialization servicestack msgpack messagepack

我正在尝试在当前的Servicestack实现中支持MessagePack协议。我需要它来支持(de)序列化像这样定义的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。怎么做的?

1 个答案:

答案 0 :(得分: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);