Bitte lesen Sie den folgenden Code, der XML ausgibt, um eine einfache Klasse mit einer Liste von 3 Objekten zu archivieren. Die 3 Objekte in der Liste fallen voneinander ab, Base, Derived1, Derived2. Ich verwende XMLArrayItemAttributes, um die Namen während der Serialisierung zu überschreiben. Dies funktioniert in .NET 3.0, gibt aber jetzt ein anderes Ergebnis in .NET 4.0 aus. Bitte beachten Sie die folgenden Ausgaben, insbesondere den zweiten Nachkommen Item DerivedItem2.XML-Serialisierung - anderes Ergebnis in. NET 4.0
Hat jemand irgendwelche Erfahrung damit und wie ich es beheben könnte, um in .NET 4.0 zu arbeiten, wie es in v3.5 getan hat?
Es scheint, dass ich die Reihenfolge nicht steuern kann, in der die Array-Elemente überschrieben werden. Es scheint nicht die Reihenfolge zu sein, in der sie den XMLArrayItems hinzugefügt werden.
Edit: Ich habe gerade das gleiche Beispiel mit MONO gegen Framework-Versionen 4.0 und 4.5 versucht und es funktioniert gut mit denen. Könnte das nur ein Fehler bei den Microsoft Framework-Versionen sein?
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);
}
}
Output Original (.NET 3.0):
<?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>
Output geändert (.NET 4.0):
<?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>
Update: Ausgabe von .NET 4,5
<?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>
Aktualisierung: Abbiegen o Im Debugging-Schalter in der app.config wie unten, referenziert von http://msdn.microsoft.com/en-us/library/aa302290.aspx, finde ich, dass die Reihenfolge, in der die Serialisierung die Überschreibungen anwendet, sich von der Reihenfolge unterscheidet, in der ich das Override-Array fülle. Hat jemand eine Idee, wie diese Bestellung bestimmt oder außer Kraft gesetzt wird?
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4" />
</switches>
</system.diagnostics>
</configuration>
Das gibt mir # Ausgabedatei ac die die Überschreibungsreihenfolge als falsch zeigt:
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>)[email protected]);
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);
}
Ja, das scheint seltsam. Hast du es mit .net 4.5 versucht? Die Reihenfolge, in der die Art von "ai" überprüft wird, sollte meiner Meinung nach von der am meisten spezialisierten zur allgemeinsten Art sein. Alles andere macht keinen Sinn, da es niemals den richtigen Typnamen in der einschließenden XML ausgibt, und daher wird das Deserialisieren nicht funktionieren. Haben Sie versucht, das generierte XML zu deserialisieren? Erhalten Sie das gleiche Verhalten, wenn Sie WriteXml() nicht überschreiben? – Rory
Der Serialisierer könnte einige knifflige Aspekte haben, z.B. Haben Sie versucht, sicherzustellen, dass Ihre abgeleiteten Klassen alle über eine ReadXml() -Methode verfügen? – Rory
Danke für die Kommentare Rory. Ich habe ein paar Kombinationen aus dem Einschließen/Ausschließen von ReadXml/WriteXml ausprobiert und es scheint immer noch dieselben Basiselementnamen zu geben. Habe es auch mit .NET 4.5 sowohl im Legacy-Modus als auch nicht ausprobiert, aber immer noch keinen Unterschied. –