我正在尝试序列化和反序列化Node对象树。我的抽象“Node”类以及从中派生的其他抽象和具体类在我的“Informa”项目中定义。另外,我在Informa中创建了一个静态类,用于序列化/反序列化。
首先,我将我的树解构为类型字典(guid,Node)的平面列表,其中guid是Node的唯一ID。
我能够在没有问题的情况下序列化我的所有节点。但是当我尝试反序列化时,我得到以下异常。
第1行位置错误227.元素 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value' 包含的数据 'Informa:建立'数据合同。该 deserializer没有任何知识 映射到此合同的类型。加 与“建筑”对应的类型 到已知类型列表 - for 例如,通过使用 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;
}
}
答案 0 :(得分:3)
所有派生自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 :(得分:1)
好的,这是一个应该让事情更清晰的图表。我正在为另一个程序开发一个插件,它添加了程序中尚未包含的关系和属性。这些关系/属性在我的树结构中定义。但是,我试图抽象地定义这个结构,以便我可以为不同的程序创建插件的实现,以及在单个“查看器”程序中访问来自多个实现的信息。
我的Serialize / Deserialize方法在框架中定义,但框架并不了解所有实现。我希望我可以避免让实现项目将类型列表传递给框架项目中的save / open / serialize / deserialize方法,但似乎我无法避免这种情况?我想这是有意义的,序列化/反序列化方法必须能够访问它们反序列化的类型。
http://dl.getdropbox.com/u/113068/informa_framework.jpg&lt; ---更大的版本 alt text http://dl.getdropbox.com/u/113068/informa_framework.jpg
所以这并没有真正解释Building的问题,因为它是框架项目中的具体类。我认为当我序列化DataContractSerializer可以访问所有对象及其KnowType参数并正确保存时,会发生什么。但是当我反序列化时,我使用Dictionary作为类型创建我的DataContractSerializer。所以它只知道节点,但不知道派生的节点类。
new DataContractSerializer(typeof(Dictionary<Guid, Node>))
我无法告诉它所有派生类型是什么,因为我说它们在框架项目不知道的其他项目中。 Soooooooo似乎是最好的解决方案,让每个实现都将它使用的Node类型列表传递给框架项目中的序列化和反序列化方法。
这有意义吗?有更好的方法吗?
答案 2 :(得分: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;}
}