1

我在使用 protobuf.net 序列化对象时遇到问题。我已经在其他课程上使用过它并且效果很好,但是使用它却不行。

能不能帮我说说原因。谢谢。

我想使用 protobuf,因为 BinaryFormatter 在序列化/反序列化方面非常慢。

这是课程:

using System.Collections.Generic;
using System;
using ProtoBuf;
using System.Xml.Serialization;
using System.Runtime.Serialization;

namespace RadixTree
{
[Serializable, DataContract, ProtoContract]
public class Node<T>
{
    private readonly List<Node<T>> children = new List<Node<T>>();
    private readonly string key;
    private T value;
    public Node(string key, T value)
    {
        this.key = key;
        this.value = value;
    }
    private Node()
    {
    }
    protected bool HasChildren
    {
        get { return children.Count > 0; }
    }
    public void Insert(string key, T value)
    {
        var potentialChild = new Node<T>(key, value);
        Add(potentialChild);
    }
    private bool Add(Node<T> theNewChild)
    {
        if (Contains(theNewChild))
            throw new DuplicateKeyException(string.Format("Duplicate key: '{0}'", theNewChild.key));

        if (!IsParentOf(theNewChild))
            return false;

        bool childrenObligedRequest = RequestChildrenToOwn(theNewChild);
        if (childrenObligedRequest) return true;

        AcceptAsOwnChild(theNewChild);
        return true;
    }
    private bool RequestChildrenToOwn(Node<T> newChild)
    {
        return
            children.Exists(
                existingChild =>
                existingChild.MergeIfSameAs(newChild) || existingChild.Add(newChild) ||
                ForkANewChildAndAddChildren(existingChild, newChild));
    }
    private bool MergeIfSameAs(Node<T> potentialChild)
    {
        if (!IsTheSameAs(potentialChild) || IsNotUnrealNode())
            return false;

        value = potentialChild.value;
        return true;
    }
    private bool IsNotUnrealNode()
    {
        return !IsUnrealNode();
    }
    private void Disown(Node<T> existingChild)
    {
        children.Remove(existingChild);
    }
    private Node<T> AcceptAsOwnChild(Node<T> child)
    {
        if (NotItself(child)) children.Add(child);
        return this;
    }
    private bool NotItself(Node<T> child)
    {
        return !Equals(child);
    }
    private bool ForkANewChildAndAddChildren(Node<T> existingChild, Node<T> newChild)
    {
        if (existingChild.IsNotMySibling(newChild))
            return false;

        var surrogateParent = MakeASurrogateParent(existingChild, newChild);
        if (surrogateParent.IsTheSameAs(this))
            return false;

        SwapChildren(existingChild, newChild, surrogateParent);
        return true;
    }
    private bool IsNotMySibling(Node<T> newChild)
    {
        return !IsMySibling(newChild);
    }
    private void SwapChildren(Node<T> existingChild, Node<T> newChild, Node<T> surrogateParent)
    {
        surrogateParent.AcceptAsOwnChild(existingChild)
            .AcceptAsOwnChild(newChild);

        AcceptAsOwnChild(surrogateParent);
        Disown(existingChild);
    }
    private Node<T> MakeASurrogateParent(Node<T> existingChild, Node<T> newChild)
    {
        string keyForNewParent = existingChild.CommonBeginningInKeys(newChild);
        keyForNewParent = keyForNewParent.Trim();
        var surrogateParent = new Node<T>(keyForNewParent, default(T));

        return surrogateParent.IsTheSameAs(newChild) ? newChild : surrogateParent;
    }
    private bool IsTheSameAs(Node<T> parent)
    {
        return Equals(parent);
    }
    private bool IsMySibling(Node<T> potentialSibling)
    {
        return CommonBeginningInKeys(potentialSibling).Length > 0;
    }
    private string CommonBeginningInKeys(Node<T> potentialSibling)
    {
        return key.CommonBeginningWith(potentialSibling.key);
    }
    internal virtual bool IsParentOf(Node<T> potentialChild)
    {
        return potentialChild.key.StartsWith(key);
    }
    public bool Delete(string key)
    {
        Node<T> nodeToBeDeleted = children.Find(child => child.Find(key) != null);
        if (nodeToBeDeleted == null) return false;

        if (nodeToBeDeleted.HasChildren)
        {
            nodeToBeDeleted.MarkAsUnreal();
            return true;
        }

        children.Remove(nodeToBeDeleted);
        return true;
    }
    private void MarkAsUnreal()
    {
        value = default(T);
    }
    public T Find(string key)
    {
        var childBeingSearchedFor = new Node<T>(key, default(T));
        return Find(childBeingSearchedFor);
    }
    private T Find(Node<T> childBeingSearchedFor)
    {
        if (Equals(childBeingSearchedFor)) return value;
        T node = default(T);
        children.Find(child =>
                          {
                              node = child.Find(childBeingSearchedFor);
                              return node != null;
                          });
        if (node == null) return default(T);
        return node;
    }
    public bool Contains(string key)
    {
        return Contains(new Node<T>(key, default(T)));
    }
    private bool Contains(Node<T> child)
    {
        if (Equals(child) && IsUnrealNode()) return false;

        if (Equals(child)) return true;

        return children.Exists(node => node.Contains(child));
    }
    private bool IsUnrealNode()
    {
        return value == null;
    }
    public List<T> Search(string keyPrefix)
    {
        var nodeBeingSearchedFor = new Node<T>(keyPrefix, default(T));
        return Search(nodeBeingSearchedFor);
    }
    private List<T> Search(Node<T> nodeBeingSearchedFor)
    {
        if (IsTheSameAs(nodeBeingSearchedFor))
            return MeAndMyDescendants();

        return SearchInMyChildren(nodeBeingSearchedFor);
    }
    private List<T> SearchInMyChildren(Node<T> nodeBeingSearchedFor)
    {
        List<T> searchResults = null;

        children.Exists(existingChild => (searchResults = existingChild.SearchUpAndDown(nodeBeingSearchedFor)).Count > 0);

        return searchResults;
    }
    private List<T> SearchUpAndDown(Node<T> node)
    {
        if (node.IsParentOf(this))
            return MeAndMyDescendants();

        return IsParentOf(node) ? Search(node) : new List<T>();

    }
    private List<T> MeAndMyDescendants()
    {
        var meAndMyDescendants = new List<T>();
        if (!IsUnrealNode())
            meAndMyDescendants.Add(value);

        children.ForEach(child => meAndMyDescendants.AddRange(child.MeAndMyDescendants()));
        return meAndMyDescendants;
    }
    public long Size()
    {
        const long size = 0;
        return Size(size);
    }
    private long Size(long size)
    {
        if (!IsUnrealNode())
            size++;

        children.ForEach(node => size += node.Size());
        return size;
    }
    public override string ToString()
    {
        return key;
    }
    public bool Equals(Node<T> other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other.key, key);
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (Node<T>)) return false;
        return Equals((Node<T>) obj);
    }
    public override int GetHashCode()
    {
        return (key != null ? key.GetHashCode() : 0);
    }
    public static Node<T> Root()
    {
        return new RootNode<T>();
    }
    public List<Node<T>> getChildren()
    {
        return children;
    }
    [Serializable, DataContract, ProtoContract]
    private class RootNode<T> : Node<T>
    {
        public RootNode() { }
        internal override bool IsParentOf(Node<T> potentialChild)
        {
            return true;
        }
    }

}
}
4

1 回答 1

1

DataContractSerializer因为 protobuf-net以及诸如此类的东西XmlSerializer不仅仅适用于字段。它需要有关要序列化哪些字段以及如何识别它们的信息(尽管有一个隐式选项,但我尽量不推荐它)。例如:

[ProtoMember(3)] private readonly List<Node<T>> children = new List<Node<T>>();
[ProtoMember(1)] private readonly string key;
[ProtoMember(2)] private T value;

然后应该可以正常工作。

(还有其他方法可以指示要序列化的成员 - 属性只是最方便的;对于信息,同样适用于[DataMember(Order=n)],因为您的类型也被标记为 a [DataContract]


以下对我来说很好:

[Test]
public void Main()
{
    Node<int> tree = new Node<int>("abc", 1), clone;
    var children = tree.getChildren();
    children.Add(new Node<int>("abc/def", 2));
    children.Add(new Node<int>("abc/ghi", 3));
    Assert.AreEqual(2, tree.getChildren().Count);

    using(var ms = new MemoryStream())
    {
        Serializer.Serialize(ms, tree);
        Assert.Greater(1, 0); // I always get these args the wrong way around, 
        Assert.Greater(ms.Length, 0); // so I always double-check!
        ms.Position = 0;
        clone = Serializer.Deserialize<Node<int>>(ms);
    }

    Assert.AreEqual("abc", clone.Key);
    Assert.AreEqual(1, clone.Value);
    children = clone.getChildren();
    Assert.AreEqual(2, children.Count);

    Assert.IsFalse(children[0].HasChildren);
    Assert.AreEqual("abc/def", children[0].Key);
    Assert.AreEqual(2, children[0].Value);

    Assert.IsFalse(children[1].HasChildren);
    Assert.AreEqual("abc/ghi", children[1].Key);
    Assert.AreEqual(3, children[1].Value);
}

编辑以下项目示例:

首先,请注意RootNode<T>实际上应该只是- 嵌套类型已经从包含类型RootNode继承。T

这里有两个问题:

首先,有一个问题RootNode——你是对的,需要声明这种关系,但是 C# 编译器不喜欢属性中的泛型。坦率地说,我会说封装根而不是继承。如果你必须继承,那很痛苦,你必须在运行时声明它,即

RuntimeTypeModel.Default.Add(typeof(Node<MyDto>), true)
     .AddSubType(4, typeof(Node<MyDto>.RootNode));

第二个问题是嵌套列表/数组;children将是一个List<List<YourType>>. 目前,不支持这种情况,虽然我有点困惑为什么它没有引发异常——它是为了抛出一个NotSupportedException引用:

不支持嵌套或锯齿状列表和数组

我会调查为什么它没有提出这个问题!

这里的问题是protobuf规范(在我的控制之外)没有办法表示这样的数据,除非中间有一条消息,即

[ProtoContract]
class SomeNewType {
    [ProtoMember(1)]
    public List<MyDto> Items {get {return items;}}
    private readonly List<MyDto> items = new List<MyDto>();
}

并使用Node<SomeNewType>而不是Node<List<MyDto>>.

理论上,protobuf-net 可以假装该层在中间,然后继续进行——但很简单:我还没有时间设计/编写/测试执行此操作所需的代码。

于 2012-06-21T08:39:34.430 回答