2013-07-25 8 views
22

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); 
} 
+0

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

+0

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

+0

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. –

Antwort

4

Well Pat,

ich es geschafft haben, das gleiche Problem zu reproduzieren, wenn in Ihrem Code zu testen. Net4 und dann zu .Net4.5 wechseln ...

In .Net4.5 scheint die Ausgabe die gleiche wie das, was Sie für .Net3

zitiert haben 0

So gehen Sie einfach und überspringen. Net4 und stattdessen einfach verwenden. Net4.5

Ursache für dieses Problem entsteht, wie Objekte im Speicher in den Frameworks erstellt werden. In .net4 werden sie wahrscheinlich von "base" zu "derived" gehalten und in .Net3 und .Net4.5 werden sie gehalten (korrekter meiner Meinung nach und es ist eine Frage der Meinung) von "abgeleitet" zu "base" . Spezifischer darauf bin ich der Meinung, dass in:

.Net4 das Framework die Objektinstanz als Typ der Basis mit Zeiger auf abgeleitete Instanz speichert.

.Net4.5/.Net3 Das Framework speichert die Objektinstanz als Typ von abgeleitet mit Zeigern zur Basisinstanz.

In beiden Fällen erhalten Sie das gleiche Ergebnis, wenn Sie in regelmäßigen Szenarien mit dem Objekt arbeiten.

Ich erinnere mich zu lesen, dass Müll sammeln einige Verbesserungen in .net4.5 hatte und ich glaube, dass dies nur ein Teil der Dinge ist, die die MS-Entwickler geändert haben, um die Leistung zu optimieren.

In beiden Tests arbeitete ich mit der gleichen Version von XML-Serializer (4.0)

+0

10 G.Y, tut mir leid, ich habe diese Frage eine Weile nicht angeschaut. Danke für die Information. Ich bin ziemlich sicher, dass ich mit .NET 4.5 nach meinem Kommentar zur ursprünglichen Frage (12. August) getestet habe. Ich werde zurückgehen und das wiederholen müssen. Ich bin gleich wieder bei Ihnen. –

+0

Ich habe gerade die App unter .NET 4.5 ausgeführt, siehe edit zur Originalfrage mit dem Ergebnis. Obwohl es ein anderes Ergebnis ist, ist es immer noch nicht behoben. –