这里的诀窍是使用这两种机制来进行序列化和反序列化。
在下面的示例中,我有一个 Foo 类型的 List ,它是 IXmlSerializable ,它有一个抽象的孩子。每个具体的子节点都使用更整洁的属性来实现序列化。
对于写入,Foos WriteXml 写出一个元素,其名称是合约子的完整类型名称。然后序列化孩子。
对于阅读,Foos ReadXml 会进行一些丑陋的读取以将自己定位在全名所在的位置,并创建具体类型的新实例(但请注意,Foo 不知道所有可能的具体类型)
然后它基于具体类型创建一个新的 XmlSerializer 并将其反序列化。
再次,它需要做一些丑陋的读取来定位下一个元素的阅读器。
这会产生以下 XML
<?xml version="1.0"?>
<ArrayOfFoo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo>
<ParentData>a</ParentData>
<XmlAbstractSerialisationTest.Bar1>
<Bar1>
<Id>0</Id>
<Name>hello</Name>
<Extra1>boo</Extra1>
<Extra2>good</Extra2>
</Bar1>
</XmlAbstractSerialisationTest.Bar1>
</Foo>
<Foo>
<ParentData>b</ParentData>
<XmlAbstractSerialisationTest.Bar2>
<Bar2>
<Id>0</Id>
<Name>hello</Name>
<Extra1>boo</Extra1>
<Extra2>123</Extra2>
</Bar2>
</XmlAbstractSerialisationTest.Bar2>
</Foo>
</ArrayOfFoo>
代码是:
namespace XmlAbstractSerialisationTest
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
class Program
{
static void Main(string[] args)
{
// Write
List<Foo> data = new List<Foo>{
new Foo{
ParentData = "a",
Child = new Bar1{
Id = 0,
Name = "hello",
Extra1 = "boo",
Extra2 = "good"
}
},
new Foo{
ParentData = "b",
Child = new Bar2{
Id = 0,
Name = "hello",
Extra1 = "boo",
Extra2 = 123
}
}
};
XmlSerializer xs = new XmlSerializer(typeof(List<Foo>));
using (FileStream fs = new FileStream("test.xml", FileMode.Create, FileAccess.Write))
{
xs.Serialize(fs, data);
}
// Read
List<Foo> newData;
using (FileStream fs = new FileStream("test.xml", FileMode.Open, FileAccess.Read))
{
newData = xs.Deserialize(fs) as List<Foo>;
}
}
}
public class Foo : IXmlSerializable
{
public string ParentData { get; set; }
public BaseBar Child { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new System.NotImplementedException();
}
public void ReadXml(System.Xml.XmlReader reader)
{
reader.Read();
reader.Read();
ParentData = reader.Value;
reader.Read();
reader.Read();
Assembly ass = Assembly.LoadFile(@"[Full path to assembly]");
Child = ass.CreateInstance(reader.Name) as BaseBar;
reader.Read();
XmlSerializer xs = new XmlSerializer(Child.GetType());
Child = xs.Deserialize(reader) as BaseBar;
reader.Read();
reader.Read();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteElementString("ParentData", ParentData);
writer.WriteStartElement(Child.GetType().FullName);
XmlSerializer xs = new XmlSerializer(Child.GetType());
xs.Serialize(writer, Child);
writer.WriteEndElement();
}
}
[Serializable]
public abstract class BaseBar
{
[XmlElement]
public int Id { get; set; }
[XmlElement]
public string Name { get; set; }
}
[Serializable]
public class Bar1 : BaseBar
{
[XmlElement]
public string Extra1 { get; set; }
[XmlElement]
public string Extra2 { get; set; }
}
[Serializable]
public class Bar2 : BaseBar
{
[XmlElement]
public string Extra1 { get; set; }
[XmlElement]
public int Extra2 { get; set; }
}
}
上面的代码可以序列化和反序列化任意复杂的具体类型,而父类不知道这些类型。
每次处理具体类型时,都需要序列化全名。然后反序列化必须知道全名(编码到元素名称中)和可以找到类型的程序集。
上面的代码有问题!
如果具体类型具有非默认构造函数,则 CreateInstance 方法将失败。
可能它可以使用 FormatterServices.GetUninitializedObject
明天我会玩这个。
此代码应该可以工作,但您需要将“[程序集的完整路径]”更新为正确的程序集路径。