2013-07-25 32 views
22

Proszę zobaczyć poniższy kod, który wypisze XML, aby złożyć prostą klasę zawierającą listę 3 obiektów. Trzy obiekty na liście pochodzą od siebie, Base, Derived1, Derived2. Używam XMLArrayItemAttributes do nadpisania nazw podczas Serialization. To działa dobrze w .NET 3.0, ale teraz daje inny wynik w .NET 4.0. Proszę zobaczyć poniższe Wyniki, zwracając uwagę na drugi element potomny DerivedItem2.Serializacja XML - inny wynik w .NET 4.0

Czy ktoś ma doświadczenie z tym i jak mogę go naprawić, aby pracować w .NET 4.0, tak jak to miało miejsce w wersji 3.5?

Wygląda na to, że nie mogę kontrolować kolejności, w której elementy tablicy są nadpisywane. Wydaje się, że nie jest to kolejność, w jakiej są dodawane do elementów XMLArrayItems.

Edycja: Właśnie wypróbowałem ten sam przykład używając MONO wobec wersji framework 4.0 i 4.5 i to działa dobrze z tymi. Czy to może być błąd w wersjach frameworków Microsoftu?

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

Wyjście 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> 

Wyjście Zmienione (.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: Wyjście z .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> 

Aktualizacja: Włączanie o W przełączniku debugowania w pliku app.config, jak to opisano poniżej, pod numerem http://msdn.microsoft.com/en-us/library/aa302290.aspx, stwierdzam, że kolejność, w jakiej szeregowanie dotyczy nadpisań, jest inna niż kolejność, w której wypełniam tablicę przesłaniania. Czy ktoś ma pojęcie, w jaki sposób ustalana jest ta kolejność?

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

To daje mi aC# plik wyjściowy, który pokazuje kolejność override jako niepoprawny:

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

Taa, to wydaje się dziwne. Czy próbowałeś z .net 4.5? Kolejność typów "ai" jest po prostu od najbardziej wyspecjalizowanych do najbardziej uogólnionych, zgodnie z moim myśleniem. Coś innego nie ma sensu, ponieważ nigdy nie wypisze nazwy odpowiedniego typu w załączonym kodzie XML, a zatem deserialising nie zadziała. Czy próbowałeś ponownie deserializować wygenerowanego XML? Czy zachowujesz się tak samo, jeśli nie zastąpisz WriteXml()? – Rory

+0

Może być jakiś drobny aspekt dla serializera, np. czy próbowałeś upewnić się, że wszystkie klasy pochodne mają metodę ReadXml()? – Rory

+0

Dzięki za komentarze Rory. Próbowałem kilka kombinacji włączania/wyłączania ReadXml/WriteXml i nadal wydaje się, że dają te same nazwy elementów podstawowych. Próbowałem również z .NET 4.5 w obu starszym trybie i nie, ale nadal nie ma różnicy. –

Odpowiedz

4

Well Pat

udało mi się odtworzyć ten sam problem podczas testowania kodu w. Net4 i zmiana na .Net4.5 ...

W .Net4.5 dane wyjściowe wydają się być takie same, jak podane dla .Net3

Po prostu przejdź dalej i pomiń .Net4, a zamiast tego po prostu użyj .Net4.5

Powód tego problemu wynika z konstrukcji obiektów w pamięci w frameworkach. W .net4 prawdopodobnie przechowuje się je od "bazy" do "pochodnych" oraz w .Net3 i .Net4.5 są one przechowywane (dokładniej moim zdaniem i to jest kwestia opinii) od "pochodnych" do "bazowych" . Mówiąc dokładniej, uważam, że w:

.Net4 framework przechowuje instancję obiektu jako typ bazy ze wskaźnikiem do instancji pochodnej.

.Net4.5/.Net3 framework przechowuje instancję obiektu jako typ wyprowadzony ze wskaźnikami do instancji bazowej.

W obu przypadkach uzyskuje się ten sam wynik podczas pracy z obiektem w regularnych sytuacjach.

Pamiętam, że czytanie, że zbieranie śmieci miało pewne ulepszenia w .net4.5 i wierzę, że to tylko część rzeczy, które twórcy MS zmienili, aby zoptymalizować wydajność.

W obu testach pracowałem z tą samą wersją Serializera XML (4.0)

+0

G.Y, przepraszam, nie patrzyłem na to pytanie przez chwilę. Dzięki za informację. Jestem dość pewny testowałem z .NET 4.5, jak na mój komentarz na oryginalne pytanie (12 sierpnia). Będę musiał wrócić i spróbować ponownie. Wrócę do Ciebie wkrótce. –

+0

Właśnie uruchomiłem aplikację w .NET 4.5, zobacz edycję oryginalnego pytania z wynikiem. Chociaż jest to inny wynik, nadal nie jest naprawiony. –