2013-07-25 16 views
22

Vedere il codice seguente che scrive XML per archiviare una semplice classe contenente un elenco di 3 oggetti. I 3 oggetti nella lista scendono l'uno dall'altro, Base, Derivato1, Derivato2. Io uso XMLArrayItemAttributes per sovrascrivere i nomi durante la serializzazione. Funziona correttamente con .NET 3.0, ma ora genera un risultato diverso in .NET 4.0. Si prega di vedere i risultati di seguito, prendendo atto in particolare del secondo elemento discendente DerivedItem2.- risultato diverso in .NET 4.0

Qualcuno ha qualche esperienza con questo e come posso risolvere il problema per funzionare in .NET 4.0 come ha fatto nella v3.5?

Sembra che non possa controllare l'ordine in cui gli elementi dell'array sono sovrascritti. Non sembra essere l'ordine in cui vengono aggiunti a XMLArrayItems.

Modifica: Ho appena provato lo stesso esempio utilizzando MONO rispetto alle versioni 4.0 e 4.5 e funziona perfettamente con quelli. Questo potrebbe essere un bug con le versioni del framework Microsoft?

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

uscita originale (.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> 

uscita Modificato (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> 

Aggiornamento: Uscita dal .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> 

Aggiornamento: Tornitura o n l'interruttore di debug in app.config come di seguito, a cui si fa riferimento da http://msdn.microsoft.com/en-us/library/aa302290.aspx, trovo che l'ordine in cui la serializzazione applica le sostituzioni è diverso dall'ordine in cui riempio l'array di override. Qualcuno ha idea di come questo ordine è determinato o sovrascritto?

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <system.diagnostics> 
     <switches> 
      <add name="XmlSerialization.Compilation" value="4" /> 
     </switches> 
    </system.diagnostics> 
</configuration> 

Questo mi dà file di aC# uscita che mostra l'ordine di esclusione come non corretta:

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

Sì, questo sembra strano. Hai provato con .net 4.5? L'ordine in cui il tipo di 'ai' è controllato dovrebbe essere solo dal tipo più specializzato a quello più generalizzato, secondo il mio modo di pensare. Qualcos'altro non ha senso in quanto non produrrà mai il giusto nome di tipo nell'XML allegato e quindi la deserializzazione non funzionerà. Hai provato a deserializzare l'XML generato? Hai lo stesso comportamento se non esegui l'override di WriteXml()? – Rory

+0

Potrebbero esserci alcuni aspetti complessi del serializzatore, ad es. hai provato a garantire che le tue classi derivate abbiano tutte un metodo ReadXml()? – Rory

+0

Grazie per i commenti Rory. Ho provato alcune combinazioni di includere/escludere ReadXml/WriteXml e sembra ancora dare gli stessi nomi degli elementi base. Anche provato con .NET 4.5 in entrambe le modalità legacy e non, ma ancora nessuna differenza. –

risposta

4

Bene Pat,

sono riuscito a riprodurre lo stesso problema durante il test il codice in. Net4 e che cambiano in .Net4.5 ...

In .Net4.5 l'uscita sembra la stessa di quello che hai indicato. Net3

Quindi, basta andare avanti e saltare .Net4 e invece basta usare .Net4.5

ragione a questo problema è nata a come gli oggetti sono costruiti in memoria nei quadri. In .net4 probabilmente sono tenuti da "base" a "derivati" e in. Net3 e. Net4.5 sono tenuti (più correttamente a mio parere ed è una questione di opinione) da "derivato" a "base" . Essendo più specifico su questo, credo che in:

.Net4 il framework memorizza l'istanza dell'oggetto come tipo di base con puntatore all'istanza derivata.

.Net4.5/.Net3 il framework memorizza l'istanza dell'oggetto come tipo di derivato con puntatori all'istanza di base.

In entrambi i casi si finisce per ottenere lo stesso risultato quando si lavora con l'oggetto in scenari regolari.

Ricordo di aver letto che la garbage collection aveva alcuni miglioramenti in .net4.5 e credo che questo sia solo una parte delle cose che gli sviluppatori di MS hanno cambiato per ottimizzare le prestazioni.

In entrambi i test ho lavorato con la stessa versione di XML Serializer (4.0)

+0

G.Y, scusa non ho guardato questa domanda per un po '. Grazie per l'informazione. Sono abbastanza sicuro di aver provato con .NET 4.5 come da mio commento sulla domanda originale (12 agosto). Dovrò tornare indietro e riprovare. Tornerò da te tra poco. –

+0

Ho appena eseguito l'app su .NET 4.5, per favore vedi la modifica alla domanda originale con il risultato. Anche se è un risultato diverso, non è ancora stato risolto. –