Сериализация родового дерева с помощью protobuf-net - PullRequest
2 голосов
/ 28 февраля 2011

Я пытаюсь построить двоичное дерево с различными объектами (int, bool, list, Dictionary и т. Д.) И сериализовать / десериализовать его.

С сериализацией в двоичную форму-конвертер все идет хорошо, но с protobufnet я получил ошибки.*

Я не знаю, связана ли проблема с использованием дерева или с использованием protobuf-net.

Любой ответ мне поможет
Спасибо

Редактировать

Я пробовал две версии кода дерева, которые я нашел в сети

Первая версия Структура данных дерева в C #

Версия Seconed http://msdn.microsoft.com/en-us/library/ms379572.aspx

T в двух версиях - это объект.

Ошибка, которую я получил: Не найдено подходящей кодировки объекта по умолчанию

1 Ответ

2 голосов
/ 28 февраля 2011

Для сериализации с 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<T> там - все, что ему нужно - это IEnumerable<T> и метод 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)
  • списки могут быть не-generic, IEnumerable и Add(object) (но должен быть указан ожидаемый тип)
...