【问题标题】:Serialization of generic tree with protobuf-net使用 protobuf-net 序列化通用树
【发布时间】:2014-02-28 09:03:09
【问题描述】:

我尝试使用各种对象(int、bool、列表、字典等)构建二叉树并对其进行序列化/反序列化。

使用二进制格式器序列化它运行良好,但使用 protobufnet 我得到了错误。

我不知道是树的实现还是使用 protobuf-net 的问题。

任何答案都会帮助我
谢谢

编辑

我尝试了我在网络中找到的两个版本的树代码

第一个版本 Tree data structure in C#

第二版 http://msdn.microsoft.com/en-us/library/ms379572.aspx

两个版本中的T都是object。

我得到的错误: 找不到合适的默认对象编码

【问题讨论】:

  • 有什么错误?那棵树是什么样子的?很乐意提供帮助,但这有点模糊......
  • 我编辑了 q。我又遇到了一个错误,但我不记得是哪个。谢谢
  • 作为参考,如果您包含@marc,那么我会自动得到一些信息,告诉我您已回复。我不需要添加,因为我正在回复您的帖子。我提到它的唯一原因是我刚刚看到这个,记得回来查看。现在看...
  • 我想让它更清楚。当其中一些对象必须是 Dictionary. 时,我想将各种对象添加到树中
  • object 在不包含类型元数据的有线格式上真的很难支持。真的可以是任何东西吗?还是“这一系列已知可能性中的一个”?最后一个是可行的。

标签: serialization binary-tree generics protobuf-net


【解决方案1】:

要使用 protobuf 序列化,有一些关于如何映射成员的必要数据 - 并注意每个成员 本身 必须对 protobuf 有意义,或者必须是某种列表。

如果您可以显示您正在使用的确切树,我可以提供更多帮助,例如:

    [ProtoContract]
    class Node<T>
    {
        [ProtoMember(1)]
        public T Value { get; set; }
        [ProtoMember(2, DataFormat= DataFormat.Group)]
        public List<Node<T>> Children { get { return children; } }
        private readonly List<Node<T>> children = new List<Node<T>>();
    }

应该可以序列化。必要的数据也可以在“v2”中在运行时提供。


部分基于电子邮件对话,我对该模型有了更好的理解,我看到的重要变化是:

  • 值字段必须为序列化注解
  • 必须对类进行注释
  • 必须有无参数构造函数
  • 必须有一些东西可以用来添加和枚举子列表

最后一个很有趣;我故意决定不在那里要求完整的IList/IList&lt;T&gt; - 它只需要IEnumerable&lt;T&gt;Add(T) 方法,所以我可以添加一个仅用于序列化目的的私有包装器对象。

所以,根据邮件内容:

using System;
using System.Collections.Generic;
using ProtoBuf;


static class Program
{
    static void Main()
    {
        var tree = new NTree<int>(1);
        tree.addChild(2);
        var child = tree.addChild(3);
        tree.addChild(4);
        child.addChild(5);
        child.addChild(6).addChild(7);


        var clone = Serializer.DeepClone(tree);
        DrawTree(tree);
        Console.WriteLine();
        Console.WriteLine();
        DrawTree(clone);
    }
    static void DrawTree<T>(NTree<T> tree, int depth = 0)
    {
        var prefix = new string('\t', depth++);
        Console.WriteLine(prefix + tree.Data);
        foreach (var child in tree.Children) DrawTree(child, depth);
    }
}

[ProtoContract]
class NTree<T>
{
    [ProtoMember(1)]
    T data;
    LinkedList<NTree<T>> children;
    internal T Data { get { return data; } } // added for demo only
    internal IEnumerable<NTree<T>> Children { get { return children; } }// added for demo only
    public NTree(T data)
    {
        this.data = data;
        children = new LinkedList<NTree<T>>();
    }

    public NTree<T> addChild(T data) // changed just so I can build a tree for the demo
    {
        var child = new NTree<T>(data);
        children.AddFirst(child);
        return child;
    }

    public NTree<T> getChild(int i)
    {
        foreach (NTree<T> n in children)
            if (--i == 0) return n;
        return null;
    }

    private NTree()
    {
        children = new LinkedList<NTree<T>>();
    }

    [ProtoMember(2, DataFormat=DataFormat.Group)]
    private NodeWrapper WrappedChildren {
        get { return new NodeWrapper(children); }
    }
    private class NodeWrapper:IEnumerable<NTree<T>>
    { // only exists to help with serialization
        private readonly LinkedList<NTree<T>> nodes;

        public NodeWrapper(LinkedList<NTree<T>> nodes)
        {
            this.nodes = nodes;
        }
        public IEnumerator<NTree<T>> GetEnumerator()
        {
            return nodes.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return nodes.GetEnumerator();
        }
        public void Add(NTree<T> child) { nodes.AddLast(child); }
    }
}

哪个应该适用于大多数T,除了object。任何不寻常的对象本身都应该是数据合同。

v2 的注意事项:

  • 你不需要属性;都可以在运行时指定
  • 您不需要无参数构造函数(尽管保留一个可能是最简单的,以使children 启动变得容易)
  • 列表可以是非泛型的,IEnumerableAdd(object)(但必须指定预期的类型)

【讨论】:

  • 谢谢!刚看到,以后试试。
  • 感谢您的详细回答。在 V2 中将 Dicionaryobject > 添加到树中是否合法?
  • @marc 你说:v2 的注释:你不需要属性;都可以在运行时指定。当我事先不知道 T 时,你能告诉我如何将 Node 添加到 RuntimeTypeModel 吗?使用 Node 或 Node 很明显。
  • @Bartosz 您仍然需要获取适当的Type 信息,例如使用typeof(Node&lt;&gt;).MakeGenericType(typeOfT),如果您使用的是通用&lt;T&gt; 方法,则使用typeof(Node&lt;T&gt;)
  • 谢谢,但两种解决方案都有假设。第一:我必须提前知道typeOfT,第二我必须是通用方法。问题是当我有Node&lt;T&gt;,而T 可以是一切。我认为,必须有解决方案,因为当我像上面的例子一样添加ProtoContractProtoMember 属性时 - 然后它就可以工作了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多