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 に答える