2013-06-26 31 views
2

如何序列化一個類的Stream(或更加正確的Stream derived)數據成員?Protobuf-net:使用Stream數據成員對第三方類進行序列化

假設我們有,我們不能屬性的第三方類:

public class Fubar 
{ 
    public Fubar() { ... } 
    public string Label { get; set; } 
    public int DataType { get; set; } 
    public Stream Data { get; set; } // Where it's always actually MemoryStream 
}; 

我試圖使用protobuf網序列化類。通過例外和我提出的各種SO問題:

RuntimeTypeModel.Default.Add(typeof(Stream), true) 
    .AddSubType(1, typeof(MemoryStream)); 
RuntimeTypeModel.Default.Add(typeof(Fubar), false) 
    .Add(1, "Label") 
    .Add(2, "DataType") 
    .Add(3, "Data"); 

using (MemoryStream ms = new MemoryStream()) 
{ 
    Fubar f1 = new Fubar(); 
    /* f1 initialized */ 

    // Serialize f1 
    Serializer.SerializeWithLengthPrefix<Message>(ms, f1, PrefixStyle.Base128); 

    // Now let's de-serialize 
    ms.Position = 0; 
    Fubar f2 = Serializer.DeserializeWithLengthPrefix<Fubar>(ms, PrefixStyle.Base128); 
} 

以上運行沒有錯誤。 Label和DataType在f2中是正確的,但Data變量只是一個空的流。調試代碼我發現內存流類似於29個字節(而f1中的數據流本身大於77KiB)。

我覺得好像我錯過了一些相當微不足道的東西,但似乎無法弄清楚它會是什麼。我認爲確實可以序列化一個流數據成員。我是否也可能以某種方式指定Stream或MemoryStream類型的數據屬性?

回答

3

Stream是一個非常複雜的野獸,並沒有內置的序列化機制。你的代碼將它配置爲一個沒有感興趣成員的類型,這就是爲什麼它會變回空。

對於這種情況,我可能會創建一個代理,並與剛剛成立起來:

RuntimeTypeModel.Default.Add(typeof(Fubar), false) 
     .SetSurrogate(typeof(FubarSurrogate)); 

其中:

[ProtoContract] 
public class FubarSurrogate 
{ 
    [ProtoMember(1)] 
    public string Label { get; set; } 
    [ProtoMember(2)] 
    public int DataType { get; set; } 
    [ProtoMember(3)] 
    public byte[] Data { get; set; } 

    public static explicit operator Fubar(FubarSurrogate value) 
    { 
     if(value == null) return null; 
     return new Fubar { 
      Label = value.Label, 
      DataType = value.DataType, 
      Data = value.Data == null ? null : new MemoryStream(value.Data) 
     }; 
    } 
    public static explicit operator FubarSurrogate(Fubar value) 
    { 
     if (value == null) return null; 
     return new FubarSurrogate 
     { 
      Label = value.Label, 
      DataType = value.DataType, 
      Data = value.Data == null ? 
       null : ((MemoryStream)value.Data).ToArray() 
     }; 
    } 
} 
+0

感謝您的快速響應。那當然是我錯過的「簡單的東西」。我希望不必去替代路線,因爲涉及的實際類有20-30個屬性,根據配置/過濾器,其中一些可能或可能不會被設置/使用。 是否沒有辦法爲Stream或MemoryStream類指定某種代理來幫助序列化/反序列化? – CWoods

+0

@CWoods ...好吧,也許你可以替代流?但是這個建議讓我想要抓住我的手...... –

+0

不希望你失去雙手......只是不知道是否有一種方法可以讓助手進入基於類類型的組合我避免了整個數據類的代理路線。我會咬緊牙關,爲整個事情創造一個替代品。 – CWoods

1

不作馬克爪他自己的手離開..但是如果有其他人想要爲Stream創建一個替代品,我已經改編了Marc的替代示例:

[ProtoContract] 
public class StreamSurrogate 
{ 
    [ProtoMember(1)] 
    public byte[] Data { get; set; } 

    public static explicit operator Stream(StreamSurrogate value) 
    { 
     if (value == null) 
     { 
      return null; 
     } 

     return new MemoryStream(value.Data); 
} 

    public static explicit operator StreamSurrogate(Stream value) 
    { 
     if (value == null) 
     { 
      return null; 
     } 

     if (value is MemoryStream) 
     { 
      return new StreamSurrogate { Data = ((MemoryStream)value).ToArray() }; 
     } 
     else 
     { 
      // Probably a better way to do this... 
      StreamSurrogate ss = new StreamSurrogate(); 

      ss.Data = new byte[value.Length]; 
      value.Read(ss.Data, 0, (int)value.Length); 
      return ss; 
     } 
    } 
} 

然後對於RuntimeTypeModel:

MetaType mt2 = RuntimeTypeModel.Default.Add(typeof(Stream), true); 

    mt2.AddSubType(1, typeof(MemoryStream)); 
    mt2.SetSurrogate(typeof(StreamSurrogate)); 

在這種情況下,我的示例中的f2看起來是完全正確的!

是的,有很多潛在的麻煩試圖序列化流 - 也許我會更明智地設置MemoryStream子類型的代理,因爲我知道我的具體情況將始終使用MemoryStreams數據。不過,我在Stream上註冊代理的思路如下:

  1. 該類的數據成員是Stream。
  2. 任何流派生類都可能用於原始對象,並且大多數類可能無法重新創建。
  3. 但是,由於數據成員是Stream,因此任何Stream派生類都應該足夠用於反序列化的對象(因爲它只需支持Stream)。
  4. MemoryStream可能是大多數情況下反序列化流的最佳人選。
相關問題