XML Serialization - different result in .NET 4.0
请参阅下面的代码,该代码将XML输出到包含3个对象列表的简单类中。列表中的3个对象彼此向下依次为base、derived1和derived2。我使用xmlarayitematributes在序列化期间重写名称。这在.NET 3.0中可以正常工作,但现在在.NET 4.0中输出不同的结果。请参见下面的输出,特别注意第二个后代item derividetem2。
有没有人对此有任何经验,以及我如何将其修复为在.NET 4.0中工作,就像在v3.5中那样?
似乎我无法控制数组项被重写的顺序。似乎不是它们添加到xmlarayitems的顺序。
编辑:我刚刚尝试过使用Mono对框架版本4.0和4.5使用相同的例子,它可以很好地处理这些问题。这可能只是微软框架版本的一个缺陷吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Collections; using System.Xml.Serialization; using System.Xml; using System.Xml.Schema; using System.IO; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { TestGroup g = new TestGroup(); XmlSerializer s = new XmlSerializer(typeof(TestGroup), g.GetOverrides()); TextWriter w = new StreamWriter("c:\\#\\test.xml"); s.Serialize(w, g); w.Close(); } } public class TestGroup { public List<BaseItem> Items { get; set; } public TestGroup() { Items = new List<BaseItem>(); BaseItem b = new BaseItem(); b.BaseName ="Base Name"; Items.Add(b); DerivedItem d1 = new DerivedItem(); d1.BaseName ="D1"; d1.DerivedName ="D1"; Items.Add(d1); DerivedItem2 d2 = new DerivedItem2(); d2.BaseName ="D2"; //d2.DerivedName ="D2"; d2.Derived2Name ="D2"; Items.Add(d2); } public XmlAttributeOverrides GetOverrides() { XmlAttributes atts = new XmlAttributes(); for (int i = 0; i < Items.Count; i++) { BaseItem b = Items[i]; Type ItemType = b.GetType(); XmlArrayItemAttribute ItemAtt = new XmlArrayItemAttribute(); ItemAtt.ElementName = ItemType.Name; ItemAtt.Type = ItemType; atts.XmlArrayItems.Add(ItemAtt); } XmlAttributeOverrides attOvers = new XmlAttributeOverrides(); attOvers.Add(typeof(TestGroup),"Items", atts); return attOvers; } } public class BaseItem : IXmlSerializable { public string BaseName; public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { // not required for example } public virtual void WriteXml(XmlWriter writer) { writer.WriteElementString("BaseName", this.BaseName); } } public class DerivedItem: BaseItem { public string DerivedName; public override void WriteXml(XmlWriter writer) { base.WriteXml(writer); writer.WriteElementString("DerivedName", this.DerivedName); } } public class DerivedItem2: DerivedItem { public string Derived2Name; public override void WriteXml(XmlWriter writer) { base.WriteXml(writer); writer.WriteElementString("Derived2Name", this.Derived2Name); } } |
原始输出(.net 3.0):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?xml version="1.0" encoding="utf-8"?> <TestGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Items> <BaseItem> <BaseName>Base Name</BaseName> </BaseItem> <DerivedItem> <BaseName>D1</BaseName> <DerivedName>D1</DerivedName> </DerivedItem> <DerivedItem2> <BaseName>D2</BaseName> <DerivedName /> <Derived2Name>D2</Derived2Name> </DerivedItem2> </Items> </TestGroup> |
输出已更改(.NET 4.0):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?xml version="1.0" encoding="utf-8"?> <TestGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Items> <BaseItem> <BaseName>Base Name</BaseName> </BaseItem> <DerivedItem> <BaseName>D1</BaseName> <DerivedName>D1</DerivedName> </DerivedItem> <DerivedItem> <BaseName>D2</BaseName> <DerivedName /> <Derived2Name>D2</Derived2Name> </DerivedItem> </Items> </TestGroup> |
更新:从.NET 4.5输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?xml version="1.0" encoding="utf-8"?> <TestGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Items> <BaseItem> <BaseName>Base Name</BaseName> </BaseItem> <BaseItem> <BaseName>D1</BaseName> <DerivedName>D1</DerivedName> </BaseItem> <DerivedItem2> <BaseName>D2</BaseName> <DerivedName /> <Derived2Name>D2</Derived2Name> </DerivedItem2> </Items> </TestGroup> |
更新:打开app.config中的调试开关,如下所示,引用自http://msdn.microsoft.com/en-us/library/aa302290.aspx,我发现序列化应用覆盖的顺序与我填充覆盖数组的顺序不同。有人知道该订单是如何确定或覆盖的吗?
1 2 3 4 5 6 7 8 | <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <switches> </switches> </system.diagnostics> </configuration> |
这会给我一个C输出文件,显示覆盖顺序不正确:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | void Write2_TestGroup(string n, string ns, global::WindowsFormsApplication1.TestGroup o, bool isNullable, bool needType) { if ((object)o == null) { if (isNullable) WriteNullTagLiteral(n, ns); return; } if (!needType) { System.Type t = o.GetType(); if (t == typeof(global::WindowsFormsApplication1.TestGroup)) { } else { throw CreateUnknownTypeException(o); } } WriteStartElement(n, ns, o, false, null); if (needType) WriteXsiType(@"TestGroup", @""); { global::System.Collections.Generic.List<global::WindowsFormsApplication1.BaseItem> a = (global::System.Collections.Generic.List<global::WindowsFormsApplication1.BaseItem>)((global::System.Collections.Generic.List<global::WindowsFormsApplication1.BaseItem>)o.@Items); if (a != null){ WriteStartElement(@"Items", @"", null, false); for (int ia = 0; ia < ((System.Collections.ICollection)a).Count; ia++) { global::WindowsFormsApplication1.BaseItem ai = (global::WindowsFormsApplication1.BaseItem)a[ia]; if ((object)(ai) != null){ if (ai is global::WindowsFormsApplication1.DerivedItem) { WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::WindowsFormsApplication1.DerivedItem)ai), @"DerivedItem", @"", true, true); } else if (ai is global::WindowsFormsApplication1.BaseItem) { WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::WindowsFormsApplication1.BaseItem)ai), @"BaseItem", @"", true, true); } else if (ai is global::WindowsFormsApplication1.DerivedItem2) { WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::WindowsFormsApplication1.DerivedItem2)ai), @"DerivedItem2", @"", true, true); } else if ((object)(ai) != null){ throw CreateUnknownTypeException(ai); } } } WriteEndElement(); } } WriteEndElement(o); } |
Pat,
在.net4中测试代码时,我设法重现了同样的问题,而不是改为.net4.5…
在.net4.5中,输出似乎与.net3的报价相同。
所以,继续跳过.net4,而使用.net4.5。
这个问题的原因源于框架中对象是如何在内存中构造的。在.net4中,它们可能从"base"保持到"derived",在.net3和.net4.5中,它们从"derived"保持到"base"(在我看来更正确,这是一个观点问题)。更具体地说,我相信:
.net4框架将对象实例存储为具有指向派生实例的指针的基类型。
.net4.5/.net3框架将对象实例存储为派生类型,并带有指向基实例的指针。
在这两种情况下,在常规场景中使用对象时,最终得到相同的结果。
我记得我读到过,垃圾收集在.net4.5中有了一些改进,我相信这只是ms-devs为了优化性能而改变的事情的一部分。
在这两个测试中,我使用相同版本的XML序列化程序(4.0)