4

我正在尝试序列化和反序列化 Node 对象树。我的抽象“节点”类以及从它派生的其他抽象和具体类在我的“Informa”项目中定义。此外,我在 Informa 中创建了一个用于序列化/反序列化的静态类。

首先,我将树解构为Dictionary(guid,Node)类型的平面列表,其中 guid 是 Node 的唯一 ID。

我能够毫无问题地序列化我的所有节点。但是当我尝试反序列化时,我得到以下异常。

第 1 行位置 227 出错。元素“ http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value ”包含“Informa:Building”数据合同的数据。反序列化器不知道映射到该合约的任何类型。将与“Building”对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 或将其添加到传递给 DataContract Serializer 的已知类型列表中。

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

}
4

3 回答 3

3

从 Node 派生的所有类,包括 Building,都应用了 [KnownType(typeof(type t))] 属性。

KnownType通常应用于基本类型 - 即

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

(注意 - 您也可以在DataContractSerializer构造函数中指定已知类型,而不需要属性)

编辑您的回复

如果框架类不知道所有派生类型,那么您需要在创建序列化程序时指定已知类型:

[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

结束编辑

但是,如果没有可重现的东西(即NodeBuilding),将很难有多大帮助。

另一个奇怪的事情;树结构非常适合诸如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);
            }
        }
    }
}
于 2009-04-10T07:29:44.217 回答
1

好的,这里有一个图表,应该让事情更清楚。我正在为另一个程序开发一个插件,该插件添加了程序中尚未包含的关系和属性。这些关系/属性在我的树结构中定义。但是,我试图抽象地定义这个结构,以便我可以为不同的程序创建插件的实现,以及在单个“查看器”程序中访问来自多个实现的信息。

我的 Serialize/Deserialize 方法是在框架中定义的,但框架并不知道所有的实现。我希望我可以避免让实现项目将类型列表传递给框架项目中的保存/打开/序列化/反序列化方法,但似乎我无法避免这种情况?我想这是有道理的,序列化/反序列化方法必须能够访问它们正在反序列化的类型。

http://dl.getdropbox.com/u/113068/informa_framework.jpg <---大版本 替代文本 http://dl.getdropbox.com/u/113068/informa_framework.jpg

所以这并不能真正解释 Building 的问题,因为它是框架项目中的一个具体类。我认为发生的事情是当我序列化 DataContractSerializer 可以访问所有对象及其 KnowType 参数并正确保存它们时。但是当我反序列化时,我使用 Dictionary 作为类型创建了我的 DataContractSerializer。所以它只知道节点,而不知道节点的派生类。

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

我不能告诉它所有派生类型是什么,因为就像我说它们在框架项目不知道的其他项目中一样。Soooooooo 似乎最好的解决方案是让每个实现将它使用的节点类型列表传递给框架项目中的序列化和反序列化方法。

这有意义吗?有一个更好的方法吗?

于 2009-04-10T20:49:01.237 回答
0

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;}
}
于 2009-04-10T09:48:07.580 回答