【问题标题】:Protobuf-net serialization/deserializationProtobuf-net 序列化/反序列化
【发布时间】:2012-04-26 12:12:37
【问题描述】:

我检查过,但似乎无法看到如何将一个类直接序列化为一个字节数组,然后使用 Marc Gravell 的 protobuf-net 实现从一个字节数组反序列化。

编辑:我更改了问题并提供了代码,因为如何序列化为 byte[] 而无需通过流的原始问题无疑是微不足道的。我很抱歉。

更新的问题:有没有办法不必处理泛型,而是在通过构造函数时通过反射推断属性“MessageBody”的类型?我假设我不能序列化对象类型,对吗?当前的解决方案看起来非常繁琐,因为每次实例化一个新消息时,我都需要传入 MessageBody 的类型。有没有更时尚的解决方案?

我想出了以下内容:

class Program
{
    static void Main(string[] args)
    {
        Message<string> msg = new Message<string>("Producer", "Consumer", "Test Message");

        byte[] byteArray = msg.Serialize();
        Message<string> message = Message<string>.Deserialize(byteArray);

        Console.WriteLine("Output");
        Console.WriteLine(message.From);
        Console.WriteLine(message.To);
        Console.WriteLine(message.MessageBody);

        Console.ReadLine();

    }
}

[ProtoContract]
public class Message<T>
{
    [ProtoMember(1)]
    public string From { get; private set; }
    [ProtoMember(2)]
    public string To { get; private set; }
    [ProtoMember(3)]
    public T MessageBody { get; private set; }

    public Message()
    {

    }

    public Message(string from, string to, T messageBody)
    {
        this.From = from;
        this.To = to;
        this.MessageBody = messageBody;
    }

    public byte[] Serialize()
    {
        byte[] msgOut;

        using (var stream = new MemoryStream())
        {
            Serializer.Serialize(stream, this);
            msgOut = stream.GetBuffer();
        }

        return msgOut;
    }

    public static Message<T> Deserialize(byte[] message)
    {
        Message<T> msgOut;

        using (var stream = new MemoryStream(message))
        {
            msgOut = Serializer.Deserialize<Message<T>>(stream);
        }

        return msgOut;
    }   
}

我喜欢的是这样的:

Message newMsg = new Message("Producer", "Consumer", Foo); byte[] byteArray = newMsg.Serialize();

和 Message msg = Message.Deserialize(byteArray);

(其中 Deserialize 是一个静态方法,它总是反序列化成 Message 类型的对象,只需要知道将消息体反序列化成什么类型​​)。

【问题讨论】:

  • Protobuf.net 不是开源的吗?
  • 是的,但我不想调整源代码,因为我喜欢跟上新版本而不必进行后续调整,因为库只是一个非常小的组件,作为更大项目的一部分。
  • 一个MemoryStream只是变相的字节数组,使用它有什么问题?
  • 我知道,但想知道是否存在我可能忽略的函数重载
  • 如果你只是想序列化类,那么只需在类上方使用[Serializable]即可。它工作得很好,但是它不能与 ProtoContract 一起使用。仅使用(选择)ProtoContract 或 Serializable 功能。

标签: c# serialization bytearray protocol-buffers protobuf-net


【解决方案1】:

这里有几个不同的问题,所以我会回答我能看到的:如果我错过了什么,请告诉我。

首先,如前所述,MemoryStream 是获取字节[] 的最常见方式。这与大多数序列化程序一致 - 例如,XmlSerializer、BinaryFormatter 和 DataContractSerializer没有“作为字节 [] 重载”,但会接受 MemoryStream。

泛型:你不需要使用泛型; v1 有 Serializer.NonGeneric,它把它从你身上包装了。在 v2 中,“核心”是非泛型的,可以通过 RuntimeTypeModel.Default 访问;当然 Serializer 和 Serializer.NonGeneric 继续工作。

对于必须包含类型的问题:是的,protobuf 规范假设接收者知道他们正在接受什么类型的数据。这里一个简单的选择是使用一个简单的包装对象作为“根”对象,具有多个类型的数据属性(其中只有一个是非空的)。另一种选择可能来自通过 ProtoInclude 的内置继承支持(注意:作为实现细节,这两种方法是相同的)。

在你的具体例子中,也许考虑:

[ProtoContract]
[ProtoInclude(1, typeof(Message<Foo>))]
.... More as needed
[ProtoInclude(8, typeof(Message<Bar>))]
public abstract class Message
{   }
[ProtoContract]
public class Message<T> : Message
{
    ...
}

然后只需使用&lt;Message&gt; 进行序列化 - API 将自动创建正确的类型。

在最近的版本中,还有一个 DynamicType 选项,其中包含您的类型数据,例如:

[ProtoContract]
public class MyRoot {
    [ProtoMember(1, DynamicType=true)]
    public object Value { get; set; }
}

这适用于任何持有契约类型实例的值(但不适用于原语,理想情况下不涉及继承)。

【讨论】:

  • 马克,感谢 cmets。你的最后一个建议看起来正是我想要的。让我玩一下然后回复你。
  • @Freddy 我会亲自建议 ProtoInclude 一个,但是:不管用什么......
  • 看看两者...感谢您的建议。顺便说一下,Protobuf-net 是一个很棒的库
  • 问题:如果我使用 protoinclude 会不会限制自己必须在编译时指定 T 类型?我正在寻找一种解决方案,它基本上可以让我序列化任何类型的对象,而无需指定对象包含哪些组件,直到运行时。
  • Marc,我进行了性能测试,我认为 DynamicType 解决方案运行良好。我使用 Protobuf-net 来序列化/反序列化对延迟不重要的控制消息。我还运行另一个高吞吐量/低延迟消息流,它每秒处理超过 1000 万条消息,但基于我自己的原始类型的自定义序列化例程转换为原始字节数组,因为事实证明它更快。您的解决方案正是我正在寻找的,非常感谢您的帮助,特别是考虑到它是一个周末。
【解决方案2】:

OP 发布的代码对我来说不太适用,以下是稍微适应一下 Marc Gravell 的建议。需要从 Message 继承以防止“不允许循环继承”,并且正如 GetBuffer 下面的代码 cmets 中所述,也没有成功。

希望它对其他人有所帮助,我花了好几个小时才完成所有工作......



      [ProtoContract]
      public abstract class Message
      {
        public byte[] Serialize()
        {
          byte[] result;
          using (var stream = new MemoryStream())
          {
            Serializer.Serialize(stream, this);
            result = stream.ToArray(); //GetBuffer was giving me a Protobuf.ProtoException of "Invalid field in source data: 0" when deserializing
          }
          return result;
        }
      }

      [ProtoContract]
      public class Message : Message
      {
        [ProtoMember(1)]
        public string From { get; private set; }
        [ProtoMember(2)]
        public string To { get; private set; }
        [ProtoMember(3)]
        public T MessageBody { get; private set; }

        public Message()
        { }

        public Message(string from, string to, T messageBody)
        {
          this.From = from;
          this.To = to;
          this.MessageBody = messageBody;
        }

        public static Message Deserialize(byte[] message)
        {
          Message result;
          using (var stream = new MemoryStream(message))
          {
            result = Serializer.Deserialize>(stream);
          }
          return result;
        }
      }

【讨论】:

  • 错误是标记问题:Serializer.Deserialize>(stream);
猜你喜欢
  • 2014-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多