3

I try to build binary tree with various objects (int, bool, list, dictionary etc) and serialize/deserialzie it.

With binary formmater serialization its goes good but with protobufnet I got errors.

I dont know if the problem is with the implmenting of the tree or with the use of protobuf-net.

Any answer will help me
Thanks

Edit

I tried two version of tree code that I found in net

First version Tree data structure in C#

Seconed version http://msdn.microsoft.com/en-us/library/ms379572.aspx

The T in two versions is object.

The error that i got: No suitable Default Object encoding found

Community
  • 1
  • 1
david
  • 331
  • 2
  • 5
  • 12
  • 1
    what are the errors? and what does the tree look like? happy to help, but that is a little vague... – Marc Gravell Feb 28 '11 at 06:57
  • I edited the q. I got one more error but i dont remember which. Thanks – david Feb 28 '11 at 08:05
  • for reference if you include @marc, then I'll automatically get something telling me you've replied. I don't need to add that, since I'm replying on your *post*. Only reason I mention it is that I've only just seen this, by remembering to check back. Looking now... – Marc Gravell Feb 28 '11 at 13:26
  • I want to make it more clear. I want to add to the tree various objects when some of them must be Dictionary. – david Feb 28 '11 at 13:52
  • `object` is really hard to support on a wire format that doesn't include type metadata. Could it honestly be *anything*? or is it "one out of this known set of possibilities"? The last is doable. – Marc Gravell Feb 28 '11 at 14:33

1 Answers1

2

To serialized with protobuf, there is some necessary data about how to map the members - and note that each member itself must make sense to protobuf, or must be some kind of list.

If you can show the exact tree you are using I can help more, but for example:

    [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>>();
    }

should serialize fine. The necessary data can also be supplied at runtime in "v2".


Based in part on an email conversation, I understand the model a bit better, and the important changes I see are:

  • the value field must be annotated for serialization
  • the class must be annotated
  • there must be a parameterless constructor
  • there must be something that it to use to add and enumerate the child lists

The last is an interesting one; I made the deliberate decision not to demand full IList/IList<T> there - all it needs is IEnumerable<T> and an Add(T) method, so I can add a private wrapper object that only exists for the purposes of serialization.

So, based on the email content:

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); }
    }
}

which should work for most T you throw at it, except object. Any unusual objects should probably themselves be data-contracts.

Notes for v2:

  • you don't need the attributes; that can all be specified at runtime
  • you don't need a parameterless constructor (although it is probably easiest to keep one, to make the children initiation easy)
  • lists can be non-generic, IEnumerable and Add(object) (but the expected type must be specified)
sient
  • 600
  • 1
  • 5
  • 11
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks! just saw it, I'll try it later. – david Mar 01 '11 at 05:24
  • Thanks for the detailed answer. there any change that it will be legal to add Dicionary to tree in V2? – david Mar 01 '11 at 09:03
  • @marc You say: _notes for v2: you don't need the attributes; that can all be specified at runtime_. Could you tell me how can I add Node to RuntimeTypeModel when I don't know T in advance? With Node or Node it is obvious. – Bartosz Pierzchlewicz Oct 12 '11 at 08:33
  • @Bartosz you would still need to obtain the appropriate `Type` information, for example using `typeof(Node<>).MakeGenericType(typeOfT)`, or `typeof(Node)` if you are in a generic `` method. – Marc Gravell Oct 12 '11 at 09:31
  • Thanks, but both solutions has assumptions. First: I must know `typeOfT` in advance, second I must be in generic method. Problem is when I have `Node`, and `T` can be everything. I think, there must be solution, because when I add `ProtoContract` and `ProtoMember` attributes like in above example - then it works. – Bartosz Pierzchlewicz Oct 12 '11 at 10:05
  • @Bartosz ah, sorry - I wasn't clear - they must be added to the type-model. You don't need to know the `T` in advance; `RuntimeTypeModel.Default` has a number of methods for informing it about types on-the-fly, most usefully `Add(type, true)`. – Marc Gravell Oct 12 '11 at 10:15