如何序列化类的Stream(或更正确的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-net来序列化该类。通过我提出的例外和各种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类型的数据属性?
答案 0 :(得分: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()
};
}
}
答案 1 :(得分:1)
不要让Marc自己动手......但是如果有人想为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似乎完全正确!
是的,尝试序列化Stream有很多潜在的麻烦 - 也许我更明智的是只在MemoryStream子类型上设置代理,因为我知道我的特定情况将始终使用MemoryStreams for Data。然而,我在Stream上注册代理的思路如下: