2

如何序列化一个类的 Stream(或更准确地说是 Stream 派生的)数据成员?

假设我们有一个无法归因的 3rd Party 类:

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

以上运行没有错误。标签和数据类型在 f2 中是正确的,但数据变量只是一个空流。调试代码我看到内存流大约是 29 个字节(而 f1 本身的数据流超过 77KiB)。

我觉得好像我错过了一些相当微不足道的东西,但似乎无法弄清楚它会是什么。我假设确实可以序列化流数据成员。我是否还必须以某种方式指定 Stream 或 MemoryStream 类型的数据属性?

4

2 回答 2

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()
        };
    }
}
于 2013-06-26T20:53:44.300 回答
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 上注册代理的思路如下:

  1. 类的数据成员是 Stream。
  2. 任何 Stream 派生类都可以在原始对象上使用,并且其中大多数可能无法重新创建。
  3. 但是,由于数据成员是 Stream,因此任何 Stream 派生类都应该足以用于反序列化对象(因为它只必须支持 Stream)。
  4. 在大多数情况下,MemoryStream 可能是反序列化流的最佳候选者。
于 2013-06-27T03:47:10.203 回答