反序列化器没有任何类型的知识可以映射到此合同

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

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

我能够在没有问题的情况下序列化我的所有节点。 但是当我尝试反序列化时,我得到以下exception。

第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 nodeDic = DeconstructProject(project); Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None); //serialize DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary),"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), "InformaProject", "Informa"); Dictionary nodeDic = (Dictionary)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; } } 

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

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

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

这可以通过替换前一个示例中的null与(例如) preserveObjectReferences等结合使用。

结束编辑

但是,如果没有可重复的东西(即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 data = new Dictionary(); Type type = typeof(Dictionary); 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 clone = (Dictionary) dcs.ReadObject(xr); foreach (KeyValuePair pair in clone) { Console.WriteLine(pair.Key + ": " + pair.Value.Foo + "/" + ((Building)pair.Value).Bar); } } } } 

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

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

http://sofzh.miximages.com/c%23/informa_framework.jpg <---较大版本替代文字http://sofzh.miximages.com/c%23/informa_framework.jpg

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

 new DataContractSerializer(typeof(Dictionary)) 

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

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

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