Десериализатор не обладает знаниями любого типа, которые соответствуют этому контракту - PullRequest
3 голосов
/ 10 апреля 2009

Я пытаюсь сериализовать и десериализовать дерево объектов Node. Мой абстрактный класс "Node", а также другие абстрактные и конкретные классы, производные от него, определены в моем проекте "Informa". Кроме того, я создал статический класс в Informa для сериализации / десериализации.

Сначала я деконструирую свое дерево в плоский список типа Словарь (guid, Node) , где guid - это уникальный идентификатор Node.

Я могу сериализовать все мои узлы без проблем. Но когда я пытаюсь десериализовать, я получаю следующее исключение.

Ошибка в строке 1 позиции 227. Элемент «http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value' содержит данные Информационный договор «Информа: Строительство». десериализатор не имеет никакого знания введите, что соответствует этому контракту. добавлять тип, соответствующий «зданию» к списку известных типов - для Например, используя KnownTypeAttribute или добавив его в список известных типов передан Сериализатор DataContract.

Все классы, производные от Node, включая Building, имеют к ним атрибут [KnownType (typeof (type t))] * .

Мои методы сериализации и десериализации приведены ниже:

public static void SerializeProject(Project project, string filePath)
{
    try
    {
        Dictionary<Guid, Node> nodeDic = DeconstructProject(project);

        Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);

        //serialize

        DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary<Guid, Node>),"InformaProject","Informa");

        ser.WriteObject(stream,nodeDic);

        // Cleanup
        stream.Close();
    }
    catch (Exception e)
    {
        MessageBox.Show("There was a problem serializing " + Path.GetFileName(filePath) + ". \n\nException:" + e.Message, "Doh!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        throw e;
    }

}



public static Project DeSerializeProject(string filePath)
{
    try
    {
        Project proj;

        // Read the file back into a stream
        Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

        DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary<Guid, Node>), "InformaProject", "Informa");

        Dictionary<Guid, Node> nodeDic = (Dictionary<Guid, Node>)ser.ReadObject(stream);

        proj = ReconstructProject(nodeDic);        

        // Cleanup
        stream.Close();

        return proj;

    }
    catch (Exception e)
    {
        MessageBox.Show("There was a problem deserializing " + Path.GetFileName(filePath) + ". \n\nException:" + e.Message, "Doh!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return null;
    }

}

Ответы [ 3 ]

3 голосов
/ 10 апреля 2009

Все классы, которые происходят от Node, в том числе здание, есть Атрибут [KnownType (typeof (type t))] применяется к ним.

KnownType обычно применяется к типу base - т.е.

[DataContract, KnownType(typeof(Building)), ...]
abstract class Node { ... }

(примечание - вы также можете указать известные типы в конструкторе DataContractSerializer, не требуя атрибутов)

РЕДАКТИРОВАТЬ СВОЙ ОТВЕТ

Если класс framwork не знает обо всех производных типах, вам необходимо указать известные типы при создании сериализатора:

[DataContract] abstract class SomeBase { }
[DataContract] class Foo : SomeBase { }
[DataContract] class Bar : SomeBase { }
...
// here the knownTypes argument is important
new DataContractSerializer(typeof(SomeBase),
      new Type[] { typeof(Foo), typeof(Bar) });

Это можно объединить с (например) preserveObjectReferences и т. Д., Заменив null в предыдущем примере.

КОНЕЦ РЕДАКТИРОВАНИЯ

Однако без чего-либо воспроизводимого (т. Е. Node и Building) трудно будет сильно помочь.

Другая странная вещь; Структуры деревьев очень хорошо подходят к таким вещам, как DataContractSerializer - обычно нет необходимости сначала их выравнивать, так как деревья можно тривиально выразить в xml. Вы действительно должны сгладить это?


Пример:

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

[DataContract, KnownType(typeof(Building))]
abstract class Node {
    [DataMember]
    public int Foo {get;set;}
}
[DataContract]
class Building : Node {
    [DataMember]
    public string Bar {get;set;}
}

static class Program
{
    static void Main()
    {
        Dictionary<Guid, Node> data = new Dictionary<Guid, Node>();
        Type type = typeof(Dictionary<Guid, Node>);
        data.Add(Guid.NewGuid(), new Building { Foo = 1, Bar = "a" });
        StringWriter sw = new StringWriter();
        using (XmlWriter xw = XmlWriter.Create(sw))
        {
            DataContractSerializer dcs = new DataContractSerializer(type);
            dcs.WriteObject(xw, data);
        }

        string xml = sw.ToString();

        StringReader sr = new StringReader(xml);
        using (XmlReader xr = XmlReader.Create(sr))
        {
            DataContractSerializer dcs = new DataContractSerializer(type);
            Dictionary<Guid, Node> clone = (Dictionary<Guid, Node>)
                dcs.ReadObject(xr);
            foreach (KeyValuePair<Guid, Node> pair in clone)
            {
                Console.WriteLine(pair.Key + ": " + pair.Value.Foo + "/" +
                    ((Building)pair.Value).Bar);
            }
        }
    }
}
1 голос
/ 11 апреля 2009

Хорошо, вот диаграмма, которая должна прояснить ситуацию. Я разрабатываю плагин для другой программы, который добавляет отношения и свойства, которые еще не включены в программу. Эти отношения / свойства определены в моей древовидной структуре. Однако я пытаюсь определить эту структуру абстрактно, чтобы можно было создавать реализации плагина для разных программ, а также получать доступ к информации из нескольких реализаций в одной программе «просмотра».

Мои методы Serialize / Deserialize определены в платформе, но каркас не знает обо всех реализациях. Я надеялся, что смогу избежать того, чтобы проекты реализации передавали список типов в методы save / open / serialize / deserialize в проекте Framework, но, похоже, я не могу избежать этого? Я думаю, это имеет смысл, хотя методы сериализации / десериализации должны иметь доступ к типам, которые они десериализуют.

http://dl.getdropbox.com/u/113068/informa_framework.jpg альтернативный текст http://dl.getdropbox.com/u/113068/informa_framework.jpg

Так что это на самом деле не объясняет проблему со сборкой, так как это конкретный класс в фреймворковом проекте. Я думаю, что происходит, когда я сериализую DataContractSerializer имеет доступ ко всем объектам и их параметру KnowType и сохраняет их правильно. Но когда я десериализую, я создаю свой DataContractSerializer с типом словаря. Так что он знает только об Узлах, но не о производных классах узлов.

new DataContractSerializer(typeof(Dictionary<Guid, Node>))

Я не могу сказать, что представляют собой все производные типы, потому что, как я сказал, они есть в других проектах, о которых не знает рамочный проект. Soooooooo кажется лучшим решением, чтобы каждая реализация передавала список типов узлов, которые она использует для методов сериализации и десериализации в проекте платформы.

Имеет ли это смысл? Есть ли лучший способ сделать это?

0 голосов
/ 10 апреля 2009

В чем разница между этими двумя видами использования атрибута KnownTypes? Я не осознавал, что вы можете / хотели бы указать, что один класс является KnownType другого.

[DataContract]
[KnownType(typeof(Building))]
abstract class Node {
    [DataMember]
    public int Foo {get;set;}
}
[DataContract]
class Building : Node {
    [DataMember]
    public string Bar {get;set;}
}

[DataContract] 
[KnownType(typeof(Node))]
abstract class Node {
    [DataMember]
    public int Foo {get;set;}
}
[KnownType(typeof(Building))]
class Building : Node {
    [DataMember]
    public string Bar {get;set;}
}
...