关于c#:XML序列化 – .NET 4.0中的不同结果

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)