轮子造得差不多了
XmlSerializer 大法好
请原谅我之前的愚昧无知。我已经决定完全分离对象模型和对象的逻辑行为了。
引用使用 Id 保存,即类中不再出现对其他类的直接引用,而仅仅保存 Id。这样在序列化的时候就可以直接保存了。
然而我发现处理 Nullable 类型还是一个很麻烦的问题……
c# – Is there any way for my class to support serialisation as an XML attribute? – Stack Overflow
所以我要自己造轮子!
好吧……我决定自己实现 XmlSerializer 了
重新造轮子 TT
ref = CXuesong/XSerializer/
欢迎围观 TT
最近一直在考虑以下问题:
在使用 `XmlSerializer `以精确控制 XML 格式的同时
- 如何序列化 `Nullable<T>` 格式的数据,并将其保存为 XML 属性(而非元素)?
- 如何序列化对象的引用,使其不会以多个副本的形式保存?
- 在实现上面这两点(尤其是第一点)的时候,不会引入其他的公共辅助属性。
(例如对于第一点,可以通过一个辅助属性,实现 `Nullable<T>` 与 `string` 之间的转换,而 `string` 是可以保存为 XML 属性的。)
其中,第二个问题似乎可以使用 `DataContractSeriallizer` 来解决。然而 `DataContractSeriallizer` 会在生成 XML 时自行为对象确定 Id,并使用 `z:Id` 和`z:Ref` 来表示 Id 和 Id 引用。正如在前几篇文章中提到的那样,`DataContractSeriallizer` 对 XML 结构的控制自由度是十分有限的。
那么,如何才能圆满地解决这些问题呢?
我觉得,可以自己造轮子了。
在 XmlSerializer 与 DataContractSerializer 之间抉择
通常来说,当你想更加精确地控制生成的 XML 时,可以使用 `XmlSerializer`。如果你对生成的 XML 格式并不在意(例如所有的属性/字段均会以元素的形式保存),同时想保留对象的所有信息(包括私有属性/字段),则可以使用 `DataContractSerializer`。 Continue reading “在 XmlSerializer 与 DataContractSerializer 之间抉择”
XmlSerializer 测试 2
集合
using System; using System.Collections.Generic; using System.Xml.Serialization; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { var f = new XmlSerializer(typeof(TestClass)); using (var sw = new StringWriter()) { f.Serialize(sw, new TestClass()); using (var sr = new StringReader(sw.ToString())) { var bt = (TestClass) f.Deserialize(sr); Console.WriteLine(bt.ListIdentical()); } } } } public class TestClass { private List<int> PrivateList; public List<int> MyList; public bool ListIdentical() { return PrivateList == MyList; } public TestClass() { PrivateList = new List<int>() { 123, 567 }; MyList = PrivateList; } } }
运行结果为 `True` 。实际上,如果把 `MyList` 换成只读属性,只要其值非 null / Nothing,则程序仍可以正常运行。
也就是说,反序列化过程复用了已有的集合实例。 Continue reading “XmlSerializer 测试 2”
XmlSerializer 测试
测试源代码如下,使用 VS2013 编写。
using System; using System.Collections; using System.Collections.Generic; using System.Xml.Serialization; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { var f = new XmlSerializer(typeof(TestClass), new[] { typeof(DerivedClass1), typeof(ChildNS.DerivedClass1) }); var ns = new XmlSerializerNamespaces(); ns.Add("ns", "http://yourcompany.org/schemas/ns"); ns.Add("nsc", "http://yourcompany.org/schemas/ns/child"); ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance"); using (var sw = new StringWriter()) { f.Serialize(sw, new TestClass(), ns); Console.WriteLine(sw.ToString()); } } } public class TestClass { public BaseClass BaseTest = new BaseClass(); public BaseClass BaseTest1 = new DerivedClass1(); [XmlElement(Namespace = "http://yourcompany.org/schemas/ns/child")] public BaseClass BaseTest2 = new ChildNS.DerivedClass1(); public BaseClass[] BaseArray1 = {new BaseClass(), new DerivedClass1()}; [XmlArrayItem(typeof(BaseClass), IsNullable = true), XmlArrayItem(typeof(DerivedClass1), IsNullable = true), XmlArrayItem(typeof(DerivedClass2), IsNullable = true)] public BaseClass[] BaseArray2 = { new BaseClass(), new DerivedClass1(), null, new DerivedClass2() }; [XmlArrayItem(typeof(BaseClass), IsNullable = true)] public BaseClass[] BaseArray3 = { new BaseClass(), new DerivedClass1(), null, new DerivedClass2() }; [XmlArrayItem(typeof(BaseClass))] public List<BaseClass> BaseList = new List<BaseClass> { new BaseClass(), new DerivedClass1() }; [XmlElement(IsNullable = true)] public object NullObject1 = null; public object NullObject2 = null; //public IEnumerable BaseListEnumerable = new List<BaseClass> { new BaseClass(), new DerivedClass1() }; } public class BaseClass { [XmlAttribute("value1", Namespace = "http://yourcompany.org/schemas/ns")] public int Value1; [XmlAttribute("value2")] public int Value2; [XmlAttribute("value3", Namespace = "http://yourcompany.org/schemas/ns")] public int Value3; } public class DerivedClass1 : BaseClass { [XmlAttribute("value4", Namespace = "http://yourcompany.org/schemas/ns")] public int Value4; } public class DerivedClass2 : BaseClass { [XmlAttribute("value4", Namespace = "http://yourcompany.org/schemas/ns")] public int Value4; } namespace ChildNS { //为类型强制指定 XML 名称,以及命名空间,以避免命名冲突。 [XmlType("ChildDerivedClass", Namespace = "http://yourcompany.org/schemas/ns/child")] public class DerivedClass1 : BaseClass { public int Value5; } } }
运行结果如下
<?xml version="1.0" encoding="utf-16"?> <TestClass xmlns:ns="http://yourcompany.org/schemas/ns" xmlns:nsc="http://yourcompany.org/schemas/ns/child" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BaseTest ns:value1="0" value2="0" ns:value3="0" /> <BaseTest1 xsi:type="DerivedClass1" ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> <nsc:BaseTest2 xsi:type="nsc:ChildDerivedClass" ns:value1="0" value2="0" ns:value3="0"> <nsc:Value5>0</nsc:Value5> </nsc:BaseTest2> <BaseArray1> <BaseClass ns:value1="0" value2="0" ns:value3="0" /> <BaseClass xsi:type="DerivedClass1" ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> </BaseArray1> <BaseArray2> <BaseClass ns:value1="0" value2="0" ns:value3="0" /> <DerivedClass1 ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> <DerivedClass2 ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> </BaseArray2> <BaseArray3> <BaseClass ns:value1="0" value2="0" ns:value3="0" /> <BaseClass xsi:type="DerivedClass1" ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> <BaseClass xsi:nil="true" /> <BaseClass xsi:type="DerivedClass2" ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> </BaseArray3> <BaseList> <BaseClass ns:value1="0" value2="0" ns:value3="0" /> <BaseClass xsi:type="DerivedClass1" ns:value1="0" value2="0" ns:value3="0" ns:value4="0" /> </BaseList> <NullObject1 xsi:nil="true" /> </TestClass>
可以看出,
- 在 `XmlArrayItem(Attribute)`、`XmlElement` 等相关特性中指定 IsNullable = true 可以使得当前为 null / Nothing 的元素在序列化时产生一个包含 xsi:nil=”true” 特性的 XML 元素。
- 在特性中指定元素的命名空间时,应当指定命名空间的 URI,尽管可以稍后在 `XmlSerializerNamespaces` 中为这些 URI 指定对应的命名空间前缀。
- 需要序列化派生类时,总是 需要通过 XmlInclude 特性,或者通过 `XmlSerializer` 的构造函数显式声明序列化过程中可能用到的派生类。
- 数组的序列化:可以通过 `XmlArrayItem` 特性声明数组中可能包含的派生类类型。这些类型在序列化时会使用与之对应的元素名。如果存在未在 `XmlArrayItem` 中声明的派生类,而其基类在 `XmlArrayItem` 中声明过,则会使用基类对应的元素名,外加 `xsi:type` 特性声明实际派生类的类型。(关于这一点,可以参阅另一篇文章:MSDN中“(使用 XmlArrayItemAttribute 限定)序列化派生类”一节中的示例可能与实际有出入)
- 我该滚去修改以前的代码了。