protobuf 和 List; - 如何序列化/反序列化?

发布于 2024-07-22 13:12:23 字数 266 浏览 6 评论 0原文

我有一个 List,其中包含不同类型的对象,例如整数、字符串和自定义类型。 所有自定义类型均经过 protobuf 调整。 我现在想做的是使用 protobuf.net 序列化/反序列化这个列表。 到目前为止,我怀疑我必须显式声明每种类型,不幸的是,这对于这些混合列表构造来说是不可能的。 因为二进制格式化程序可以毫无问题地执行这些操作,所以我希望我错过了一些东西,并且您可以帮助我。 所以我的问题是如何处理 protobuf.net 中的对象。

I have a List<object> with different types of objects in it like integers, strings, and custom types. All custom types are protobuf-adjusted.
What I wanna do now is to serialize / deserialize this list with protobuf.net. Up until now I suspect that I have to declare each and every type explicitly, which is unfortunately not possible with these mixed-list constructs. Because the binary formater has no problems to do these things I hope that I missed something and that you can help me out.
So my question is how to deal with objects in protobuf.net.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

陌上青苔 2024-07-29 13:12:23

(披露:我是 protobuf-net 的作者)

BinaryFormatter 是一个基于元数据的序列化器; 即它发送有关每个序列化对象的.NET 类型信息。 protobuf-net 是一个基于契约的序列化器(XmlSerializer / DataContractSerializer 的二进制等价物,它也会拒绝这一点)。

当前没有传输任意对象的机制,因为另一端无法知道您正在发送的内容; 但是,如果您想要发送一组已知不同的不同对象类型,则可能有其他选择。 管道中还有允许运行时可扩展模式(而不仅仅是在构建时固定的属性)的工作 - 但这还远未完成。


这并不理想,但它有效......当我完成支持运行时模式的工作时,它应该更容易:

using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
    public static DataItem<T> Create<T>(T value) {
        return new DataItem<T>(value);
    }
    public object Value {
        get { return ValueImpl; }
        set { ValueImpl = value; }
    }
    protected abstract object ValueImpl {get;set;}
    protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
    public DataItem() { }
    public DataItem(T value) { Value = value; }
    [ProtoMember(1)]
    public new T Value { get; set; }
    protected override object ValueImpl {
        get { return Value; }
        set { Value = (T)value; }
    }
}
[ProtoContract]
public class Foo {
    [ProtoMember(1)]
    public string Bar { get; set; }
    public override string ToString() {
        return "Foo with Bar=" + Bar;
    }
}
static class Program {
    static void Main() {
        var items = new List<DataItem>();
        items.Add(DataItem.Create(12345));
        items.Add(DataItem.Create(DateTime.Today));
        items.Add(DataItem.Create("abcde"));
        items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
        items.Add(DataItem.Create(67890));

        // serialize and deserialize
        var clone = Serializer.DeepClone(items);
        foreach (DataItem item in clone) {
            Console.WriteLine(item.Value);
        }
    }
}

(disclosure: I'm the author of protobuf-net)

BinaryFormatter is a metadata-based serializer; i.e. it sends .NET type information about every object serialized. protobuf-net is a contract-based serializer (the binary equivalent of XmlSerializer / DataContractSerializer, which will also reject this).

There is no current mechanism for transporting arbitrary objects, since the other end will have no way of knowing what you are sending; however, if you have a known set of different object types you want to send, there may be options. There is also work in the pipeline to allow runtime-extensible schemas (rather than just attributes, which are fixed at build) - but this is far from complete.


This isn't ideal, but it works... it should be easier when I've completed the work to support runtime schemas:

using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
    public static DataItem<T> Create<T>(T value) {
        return new DataItem<T>(value);
    }
    public object Value {
        get { return ValueImpl; }
        set { ValueImpl = value; }
    }
    protected abstract object ValueImpl {get;set;}
    protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
    public DataItem() { }
    public DataItem(T value) { Value = value; }
    [ProtoMember(1)]
    public new T Value { get; set; }
    protected override object ValueImpl {
        get { return Value; }
        set { Value = (T)value; }
    }
}
[ProtoContract]
public class Foo {
    [ProtoMember(1)]
    public string Bar { get; set; }
    public override string ToString() {
        return "Foo with Bar=" + Bar;
    }
}
static class Program {
    static void Main() {
        var items = new List<DataItem>();
        items.Add(DataItem.Create(12345));
        items.Add(DataItem.Create(DateTime.Today));
        items.Add(DataItem.Create("abcde"));
        items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
        items.Add(DataItem.Create(67890));

        // serialize and deserialize
        var clone = Serializer.DeepClone(items);
        foreach (DataItem item in clone) {
            Console.WriteLine(item.Value);
        }
    }
}
我很OK 2024-07-29 13:12:23

有一种方法可以做到这一点,尽管不是一种非常干净的方法,即使用包装器对象,该包装器对象利用另一种支持任意对象的序列化机制。 我在下面使用 JSON 展示了一个示例,但正如我所说,采用不同的序列化工具似乎违背了使用 protobuf 的目的:

[DataContract]
public class ObjectWrapper
{
    [DataMember(Order = 1)]
    private readonly string _serialisedContent;

    [DataMember(Order = 2)]
    private readonly string _serialisedType;

    public object Content { get; private set; }

    [UsedImplicitly]
    private ObjectWrapper() { }

    public ObjectWrapper(object content)
    {
        _serialisedContent = JsonConvert.SerializeObject(content);
        _serialisedType = content.GetType().FullName;
        Content = content;
    }

    [ProtoAfterDeserialization]
    private void Initialise()
    {
        var type = Type.GetType(_serialisedType);

        Content = type != null
            ? JsonConvert.DeserializeObject(_serialisedContent, type)
            : JsonConvert.DeserializeObject(_serialisedContent);
    }
}

编辑:这也可以使用 C# 的内置二进制序列化来完成

[DataContract]
public class ObjectWrapper
{
    [DataMember(Order = 1)]
    private readonly string _serialisedContent;

    public object Content { get; private set; }

    [UsedImplicitly]
    private ObjectWrapper() { }

    public ObjectWrapper(object content)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(stream, content);
            stream.Flush();
            stream.Position = 0;
            _serialisedContent = Convert.ToBase64String(stream.ToArray());
        }
    }

    [ProtoAfterDeserialization]
    private void Initialise()
    {
        var data = Convert.FromBase64String(source);
        using (var stream = new MemoryStream(data))
        {
            var formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            return formatter.Deserialize(stream);
        }
    }
}

There is a way of doing this, albeit not a very clean way, by using a wrapper object that utilises another serialisation mechanism that supports arbitrary objects. I am presenting an example below using JSON but, like I said, resorting to a different serialisation tool seems like defeating the purpose of using protobuf:

[DataContract]
public class ObjectWrapper
{
    [DataMember(Order = 1)]
    private readonly string _serialisedContent;

    [DataMember(Order = 2)]
    private readonly string _serialisedType;

    public object Content { get; private set; }

    [UsedImplicitly]
    private ObjectWrapper() { }

    public ObjectWrapper(object content)
    {
        _serialisedContent = JsonConvert.SerializeObject(content);
        _serialisedType = content.GetType().FullName;
        Content = content;
    }

    [ProtoAfterDeserialization]
    private void Initialise()
    {
        var type = Type.GetType(_serialisedType);

        Content = type != null
            ? JsonConvert.DeserializeObject(_serialisedContent, type)
            : JsonConvert.DeserializeObject(_serialisedContent);
    }
}

EDIT: This can also be done using C#'s built-in binary serialisation

[DataContract]
public class ObjectWrapper
{
    [DataMember(Order = 1)]
    private readonly string _serialisedContent;

    public object Content { get; private set; }

    [UsedImplicitly]
    private ObjectWrapper() { }

    public ObjectWrapper(object content)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(stream, content);
            stream.Flush();
            stream.Position = 0;
            _serialisedContent = Convert.ToBase64String(stream.ToArray());
        }
    }

    [ProtoAfterDeserialization]
    private void Initialise()
    {
        var data = Convert.FromBase64String(source);
        using (var stream = new MemoryStream(data))
        {
            var formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            return formatter.Deserialize(stream);
        }
    }
}
来世叙缘 2024-07-29 13:12:23
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文