【问题标题】:Proto-Buf.Net and serializationProto-Buf.Net 和序列化
【发布时间】:2023-03-05 08:15:01
【问题描述】:

我在使用 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;
        }
    }

}
}

【问题讨论】:

    标签: .net c#-4.0 protobuf-net patricia-trie


    【解决方案1】:

    因为 protobuf-net 以及 DataContractSerializerXmlSerializer 等内容不仅仅适用于字段。它需要关于要序列化哪些字段以及如何识别它们的信息(虽然一个隐式选项,但我尽量不推荐它)。例如:

    [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)],因为您的类型也标记为 [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&lt;T&gt; 实际上应该只是 RootNode - 嵌套类型已经从包含类型继承了 T

    这里有两个问题:

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

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

    第二个问题是嵌套列表/数组; children 将是 List&lt;List&lt;YourType&gt;&gt;。目前,该场景不受支持,虽然我有点困惑为什么它没有引发异常 - 意味着抛出 NotSupportedException 引用:

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

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

    这里的问题是 protobuf 规范(我无法控制)无法表示此类数据,除非中间有消息,即

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

    并使用Node&lt;SomeNewType&gt; 而不是Node&lt;List&lt;MyDto&gt;&gt;

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

    【讨论】:

    • 您好,我尝试使用 ProtoMember,但它不起作用。我不知道该怎么办。我想使用 protobuf 因为 BinaryFormatter 太慢了。你可以帮帮我吗?谢谢
    • 我也尝试添加 DataMember,但它不起作用。你能告诉我如何使用隐式选项吗?
    • 我也用过这个代码:[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]。没结果。文件大小总是 0kb!! :(
    • @Mapo 我有一个基于您在 google-code 上提出的相同问题的测试 - 它工作正常。两分钟……
    • @Mapo 我们开始; 4 个集成测试:1 个简单值 (Int32),一个复杂类型但单一实例,一个 list-of-complex-type(应该错误,此测试需要工作),一个带有封装层(见TestSomeNewType)。 protobuf-net.googlecode.com/svn/trunk/Examples/Issues/… - 另外,不要忘记查看配置继承的Init() 方法中的代码
    猜你喜欢
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 2022-07-08
    • 2012-03-17
    相关资源
    最近更新 更多