2013-01-18 11 views
3

我試圖支持MessagePack協議,我目前Servicestack實現。我需要它來支持(反)序列這樣定義ISegment的列表:使用MsgPack與Servicestack:我怎麼KnownType?

[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。它是如何完成的?

回答

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>()); 

由於serialzer被註冊,含有ISegment對象可以被包裝和解壓如常。

var packer = MessagePackSerializer.Create<PathRequest>(); 
packer.Pack(stream, somePathRequest); 
stream.Position = 0; 
var unpackedPathRequest = packer.Unpack(stream); 
+0

我不再能夠測試這個答案,但它看起來不錯。 – Brannon

+0

這將拋出,如果你的任何執行類型是抽象的。增加一個額外的條件來篩選出來可修復:'。凡(!噸=> t.IsAbstract);' – Cocowalla