Подтвердить что ты не робот

Сериализация XML - отличный результат в .NET 4.0

Пожалуйста, смотрите код ниже, который записывает XML файл, чтобы записать простой класс, содержащий список из трех объектов. 3 объекта в списке сходятся друг от друга: Base, Derived1, Derived2. Я использую XMLArrayItemAttributes для переопределения имен во время сериализации. Это отлично работает в .NET 3.0, но теперь выводит другой результат в .NET 4.0. Смотрите приведенные ниже выходы, отметив, в частности, второй элемент потомка DerivedItem2.

У кого-нибудь есть опыт работы с этим и как я могу исправить его для работы в .NET 4.0, как это было в версии 3.5?

Кажется, что я не могу контролировать порядок, в котором элементы массива переопределены. Это не похоже на порядок, в котором они добавляются в XMLArrayItems.

Изменить: я только что попробовал тот же пример, используя MONO для версий 4.0 и 4.5 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);
    }
}

Выходной оригинал (.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>

Выход изменен (.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>

Обновление: вывод из .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>

Обновление: Включение переключателя отладки в app.config, как показано ниже, ссылается на http://msdn.microsoft.com/en-us/library/aa302290.aspx, я обнаружил, что порядок, в котором Serialization применяет переопределения отличается от порядка, в котором я заполняю массив переопределения. Кто-нибудь знает, как этот порядок определяется или переопределяется?

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

Это дает мне выходной файл С#, который показывает неправильный порядок переопределения:

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

Ответ 1

Хорошо Пэт,

Мне удалось воспроизвести ту же проблему при тестировании кода в .Net4, а затем перейти на .Net4.5...

В .Net4.5 вывод кажется таким же, как вы цитировали для .Net3

Так что просто продолжайте и пропустите .Net4 и вместо этого просто используйте .Net4.5

Причина этой проблемы связана с тем, как объекты создаются в памяти в рамках. В .net4 они, вероятно, удерживаются от "базы" до "производных" и в .Net3 и .Net4.5, они удерживаются (вернее, на мой взгляд, и это вопрос мнения) от "производных" до "базы". Будучи более конкретным в этом, я считаю, что в:

.Net4 структура хранит экземпляр объекта как тип базы с указателем на производный экземпляр.

.Net4.5/.Net3 структура хранит экземпляр объекта как тип производного с указателями на базовый экземпляр.

В обоих случаях вы получаете тот же результат при работе с объектом в обычных сценариях.

Я помню, что читал, что сбор мусора имел некоторые улучшения в .net4.5, и я считаю, что это всего лишь часть того, что изменили разработчики MS, чтобы оптимизировать производительность.

В обоих тестах я работал с той же версией XML Serializer (4.0)