C# Protobuf-net 序列化/反序列化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10152539/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Protobuf-net serialization/deserialization
提问by Matthias Wolf
I checked but seem to be unable to see how to directly serialize a class to a byte array and subsequently deserialize from a byte array using Marc Gravell's protobuf-net implementation.
我检查过但似乎无法看到如何直接将类序列化为字节数组,然后使用 Marc Gravell 的 protobuf-net 实现从字节数组中反序列化。
Edit: I changed the question and provided code because the original question of how to serialize into byte[] without having to go through stream was admittedly trivial. My apologies.
编辑:我更改了问题并提供了代码,因为最初的问题是如何在不必通过流的情况下序列化为 byte[] 是微不足道的。我很抱歉。
Updated Question: Is there any way to not have to deal with generics and instead infer the type of the property "MessageBody" through reflection when it is passed through the constructor? I assume I cannot serialize object type, correct? The current solution looks very cumbersome in that I need to pass in the type of the MessageBody each time I instantiate a new Message. Is there a sleeker solution to this?
更新的问题:有什么方法可以不必处理泛型,而是在通过构造函数时通过反射推断属性“MessageBody”的类型?我假设我无法序列化对象类型,对吗?当前的解决方案看起来很麻烦,因为我每次实例化一个新消息时都需要传入 MessageBody 的类型。有没有更时尚的解决方案?
I came up with the following:
我想出了以下内容:
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;
}
}
What I like to get to is something such as:
我喜欢的东西是这样的:
Message newMsg = new Message("Producer", "Consumer", Foo); byte[] byteArray = newMsg.Serialize();
Message newMsg = new Message("Producer", "Consumer", Foo); byte[] byteArray = newMsg.Serialize();
and Message msg = Message.Deserialize(byteArray);
和 Message msg = Message.Deserialize(byteArray);
(where Deserialize is a static method and it always deserializes into an object of type Message and only needs to know what type to deserialize the message body into).
(其中 Deserialize 是一个静态方法,它总是反序列化为 Message 类型的对象,只需要知道将消息体反序列化为什么类型)。
采纳答案by Marc Gravell
there's a few different questions here, so I'll answer what I can see: if I've missed anything just let me know.
这里有几个不同的问题,所以我会回答我能看到的:如果我错过了什么,请告诉我。
Firstly, as noted, a MemoryStream is the most common way of getting to a byte[]. This is consistent with most serializers - for example, XmlSerializer, BinaryFormatter and DataContractSerializer alsodon't have an "as a byte[] overload", but will accept MemoryStream.
首先,如前所述,MemoryStream 是获取 byte[] 的最常见方式。这与大多数序列化程序一致 - 例如,XmlSerializer、BinaryFormatter 和 DataContractSerializer也没有“作为 byte[] 重载”,但会接受 MemoryStream。
Generics: you don't need to use generics; v1 has Serializer.NonGeneric, which wraps this away from you. In v2, the "core" is non-generic, and can be accessed via RuntimeTypeModel.Default; of course Serializer and Serializer.NonGeneric continue to work.
泛型:你不需要使用泛型;v1 有 Serializer.NonGeneric,它把它包装起来。在 v2 中,“核心”是非通用的,可以通过 RuntimeTypeModel.Default 访问;当然 Serializer 和 Serializer.NonGeneric 继续工作。
For the issue of having to include the type: yes, the protobuf spec assumes the receiver knows what type of data they are being given. A simple option here is to use a simple wrapper object as the "root" object, with multiple typed properties for the data (only one of which is non-null). Another option might spring from the inbuilt inheritance support via ProtoInclude (note: as an implementation detail, these two approaches are identical).
对于必须包含类型的问题:是的,protobuf 规范假设接收器知道他们正在提供什么类型的数据。这里的一个简单选项是使用一个简单的包装对象作为“根”对象,并为数据提供多个类型化的属性(只有一个是非空的)。另一种选择可能来自通过 ProtoInclude 的内置继承支持(注意:作为实现细节,这两种方法是相同的)。
In your specific example, perhaps consider:
在您的具体示例中,也许可以考虑:
[ProtoContract]
[ProtoInclude(1, typeof(Message<Foo>))]
.... More as needed
[ProtoInclude(8, typeof(Message<Bar>))]
public abstract class Message
{ }
[ProtoContract]
public class Message<T> : Message
{
? ? ...
}
Then just serialize with <Message>- the API will create the right type automatically.
然后只需序列化<Message>- API 将自动创建正确的类型。
With recent builds, there is also a DynamicTypeoption that includes type data for you, for example:
在最近的版本中,还有一个DynamicType选项,其中包含类型数据,例如:
[ProtoContract]
public class MyRoot {
[ProtoMember(1, DynamicType=true)]
public object Value { get; set; }
}
This will work for any Value that holds a contract-type instance (but not for primitives, and ideally not involving inheritance).
这适用于任何持有合约类型实例的 Value(但不适用于原语,理想情况下不涉及继承)。
回答by 9swampy
The code the OP posted wouldn't quite work for me, the following is a slight adaptation taking onboard a little more of Marc Gravell's suggestions. Inheriting from Message was needed to prevent "Cyclic inheritance is not allowed", and as noted in the code comments below GetBuffer wasn't working out either.
OP 发布的代码对我来说不太适用,以下是对 Marc Gravell 的更多建议进行的轻微改编。需要从 Message 继承以防止“不允许循环继承”,并且正如 GetBuffer 下面的代码注释中所指出的那样,也没有解决。
Hoping it helps someone else, took me a good few hours to get it all to work...
希望它可以帮助其他人,我花了好几个小时才让它全部工作......
[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;
}
}

