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

Имя элемента элемента списка XmlSerializer

У меня есть класс PersonList

[XmlRoot("Persons")]
PersonList : List<Human>

когда я сериализую это в XML, по умолчанию он будет создавать что-то вроде этого:

<Persons>
  <Human>...</Human>
  <Human>...</Human>
</Persons>

Мой вопрос в том, что нужно сделать, чтобы изменить элемент Human на Person на выходе? поэтому выход будет:

<Persons>
  <Person>...</Person>
  <Person>...</Person>
</Persons>

и как десериализовать указанный выше XML объект класса PersonList?

В совете Ника, Вот мой тестовый код:

[XmlRoot("Persons")]
public class Persons : List<Human>
{

}

[XmlRoot("Person")]
public class Human
{
    public Human()
    {
    }

    public Human(string name)
    {
        Name = name;
    }

    [XmlElement("Name")]
    public string Name { get; set; }

}

void TestXmlSerialize()
{
    Persons personList = new Persons();
    personList.Add(new Human("John"));
    personList.Add(new Human("Peter"));

    try
    {
        using (StringWriter writer = new StringWriter())
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Persons));
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.OmitXmlDeclaration = true;

            XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
            namespaces.Add(string.Empty, string.Empty);

            XmlWriter xmlWriter = XmlWriter.Create(writer, settings);
            serializer.Serialize(xmlWriter, personList, namespaces);

            Console.Out.WriteLine(writer.ToString());
        }
    }
    catch (Exception e)
    {
        Console.Out.WriteLine( e.ToString());
    }
}

Вывод тестового кода:

<Persons>
  <Human>
    <Name>John</Name>
  </Human>
  <Human>
    <Name>Peter</Name>
  </Human>
</Persons>

Как показывает результат, [XmlRoot("Person")] на Human не меняет тег на Person из Human.

4b9b3361

Ответ 1

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

Если вы можете, однако, обернуть коллекцию Persons внутри другого класса, тогда вы будете иметь полный контроль над сгенерированным выходом с помощью XmlArrayAttribute и XmlArrayItemAttribute.

Если вы не можете создать этот новый класс, вы можете прибегнуть к реализации IXmlSerializable, но это намного сложнее.

Пример для первого варианта:

[XmlRoot("Context")]
public class Context
{
    public Context() { this.Persons = new Persons(); }

    [XmlArray("Persons")]
    [XmlArrayItem("Person")]
    public Persons Persons { get; set; }
}

public class Persons : List<Human> { }

public class Human
{
    public Human() { }
    public Human(string name) { Name = name; }
    public string Name { get; set; }
}

class Program
{
    public static void Main(string[] args)
    {
        Context ctx = new Context();
        ctx.Persons.Add(new Human("john"));
        ctx.Persons.Add(new Human("jane"));

        var writer = new StringWriter();
        new XmlSerializer(typeof(Context)).Serialize(writer, ctx);

        Console.WriteLine(writer.ToString());
    }
}

Ответ 2

Отметьте свой класс следующими атрибутами:

[XmlType("Account")]
[XmlRoot("Account")]

Ответ 3

У меня была идентичная проблема с моим сериализатором. Ни один из ответов выше не работал точно. Я обнаружил, что атрибут XmlRoot в классе Human явно игнорируется, поскольку он не является корневым элементом документа. Обертка списка в объекте контекста не была для меня вариантом, потому что я не могу изменить схему XML. Решение состоит в том, чтобы изменить класс лиц. Вместо того, чтобы подклассифицировать общий список, вы завертываете его в объект и изменяете его сериализацию. См. Пример кода ниже:

[XmlRoot("Persons")]
public class Persons 
{
    public Persons ()
    {
        People = new List<Human>();
    }

    [XmlElement("Person")]
    public List<Human> People 
    { get; set; }
}

public class Human
{
    public Human()
    {
    }

    public Human(string name)
    {
        Name = name;
    }

    [XmlElement("Name")]
    public string Name { get; set; }
}

Сериализация вашего общего списка с помощью XmlElement означает, что он не будет помещать элемент оболочки вокруг вашего списка, как это делает XmlArray или как это делает подкласс. Он также дает вам бонусный вариант добавления атрибутов в класс Person, из которого я получил эту идею:

Как добавить атрибут в элемент XmlArray (XML Serialization)?

Ответ 4

Это мой тестовый код

using System.Collections.Generic;
using System.Xml.Serialization;

namespace TestLoadingMultiXml
{
[XmlRoot([email protected]"main")]
public class XmlMain
{
    private XmlDataTest data;

    [XmlElement([email protected]"datalist")]
    public XmlDataTest Data
    {
        get { return data; }
        set { data = value; }
    } // public XmlDataTest Data

    public XmlMain()
    {
        data = new XmlDataTest();
    }
}

[XmlRoot([email protected]"xmldata")]
public class XmlDataTest
{
    private List<DataDetails> listData;

    [XmlElement([email protected]"listdata")]
    public List<DataDetails> Data
    {
        get { return listData; }
        set { listData = value; }
    }

    public XmlDataTest()
    {
        listData = new List<DataDetails>();
        for (int i = 0; i < 10; i++)
        {
            DataDetails d = new DataDetails(string.Format("{0}", i));
            listData.Add(d);
        } // for (int i=0; i < 10; i++)
    } // public XmlDataTest()
} // class XmlDataTest

[XmlRoot([email protected]"datadetail")]
public class DataDetails
{
    private string name;

    [XmlAttribute([email protected]"name")]
    public string Name
    {
        get
        {
            return name;
        }
        set { name = value; }
    }

    public DataDetails(string _value)
    {
        this.name = _value;
    } // public DataDetails(string _value)

    public DataDetails()
    {
        this.name = "";
    } // public DataDetails()
} // public class DataDetails
}

и запущенной программы

using System;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace TestLoadingMultiXml
{
    public partial class Form1 : Form
    {
    private XmlMain xt;
    private string xname = @"x.xml";

    public Form1()
    {
        InitializeComponent();
        this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
    }

    void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        XmlSerializer x = new XmlSerializer(typeof(XmlMain));
        FileStream fs = new FileStream(xname, FileMode.Create);
        x.Serialize(fs, xt);
        fs.Close();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        xt = new XmlMain();
        xname = Directory.GetCurrentDirectory() + @"\" + xname;
        if (File.Exists(xname))
        {
            XmlSerializer x = new XmlSerializer(typeof(XmlMain));
            FileStream fs = new FileStream(xname, FileMode.Open);
            xt = (XmlMain)x.Deserialize(fs);
            fs.Close();
        } // if (File.Exists(xname))
    }
}
}

Также это результат

<?xml version="1.0"?>
<main xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <datalist>
    <listdata name="0" />
    <listdata name="1" />
    <listdata name="2" />
    <listdata name="3" />
    <listdata name="4" />
    <listdata name="5" />
    <listdata name="6" />
    <listdata name="7" />
    <listdata name="8" />
    <listdata name="9" />
  </datalist>
</main>

Ответ 5

Задайте XmlRoot для человека:

[XmlRoot("Person")]

Боковая панель:

Люди, вероятно, должны быть людьми

Ответ 6

Есть еще одна альтернатива. Вы всегда можете реализовать IXmlSerializable в коллекциях (и любом другом классе или структуре), чтобы полностью контролировать, как элементы записываются или читаются. Многие из вас уже знают, что это обычно не рекомендуется, так как вы можете в конечном итоге выписать код "котельной плиты" вручную, который должен быть действительно автоматической логикой, указанной с атрибутами.

Для коллекций, которые должны соответствовать разумной схеме, это оправдано. Так как структура имеет жесткое ограничение здесь, а код сериализации существующего типа не должен дублироваться при правильном выполнении; т.е. не переписывать сериализацию элемента в коде коллекции, просто создайте/вызовите дочерний XmlSerializer внутри вашей реализации ReadXml/WriteXml.

Как следствие использования IXmlSerializable, это не позволяет применить XmlTypeAttribute (выдает ошибку времени выполнения, указывающую, что вы можете использовать только XmlRootAttribute). Поэтому вместо этого примените XmlSchemaProviderAttribute и верните то же самое квалифицированное имя, которое вы бы поместили в XmlTypeAttribute. Старый метод GetSchema должен возвращать null в любом случае, поскольку это был только зарезервированный метод (согласно MSDN), вероятно, потому, что они забыли включить возможность указать другое пространство имен. Лично я использую то же имя метода "GetSchema" в моем XmlSchemaProviderAttribute, поэтому оно выглядит как полное переопределение рядом с методом GetSchema, используемым в прошлом.

Конечно, лучшим решением было бы, если бы Microsoft разрешила нам применять XmlArrayItemAttribute к классам коллекции/списка и использовать это в XmlSerializer. По умолчанию он использует имя элемента типа XML в коллекциях, что, по моему мнению, является ошибкой, потому что это должно быть имя корня XML при указании или имя класса, если нет.

Когда я получу время, я вернусь и добавлю пример. Теперь рассмотрим примеры документации MSDN для IXmlSerializable и XmlSchemaProviderAttribute.

http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable(v = vs .110).aspx

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlschemaproviderattribute(v = vs .110).aspx

Ответ 7

Если у вас нет доступа к источнику для класса Human (в этом случае установка XmlRoot невозможна), вы можете создать XmlElementAttribute, а затем добавить его в XmlAttributeOverride и использовать это при создании экземпляра вашего XmlSerializer. Подробнее см. в статье MSDN.

Ответ 8

Я знаю, что это старый вопрос, но я столкнулся с той же проблемой, и ни одно из решений, похоже, не затрагивает вопрос ОП. Итак, вот мое решение (комментарии на французском языке, если вам интересно):

#region Références
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
#endregion

namespace XmlSerializationTests
{
    /// <summary>
    /// Représente une liste qui peut être sérialisée en XML en tant que noeud racine.
    /// </summary>
    /// <typeparam name="T">Type des éléments de la liste.</typeparam>
    public class XmlSerializableList<T>
        : List<T>, IXmlSerializable
    {
        #region Variables
        private static readonly XmlSerializer _ItemSerializer = new XmlSerializer(typeof(T));
        private static readonly string _ItemName;
        private string _RootName;
        #endregion

        #region Méthodes
        /// <summary>
        /// Initialisation statique
        /// </summary>
        static XmlSerializableList()
        {
            _ItemName = (typeof(T).GetCustomAttributes(typeof(XmlRootAttribute), true).FirstOrDefault() as XmlRootAttribute)?.ElementName ?? typeof(T).Name;
        }

        /// <summary>
        /// Obtient le nom racine.
        /// </summary>
        protected virtual string RootName
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_RootName)) _RootName = (GetType().GetCustomAttributes(typeof(XmlRootAttribute), true).FirstOrDefault() as XmlRootAttribute)?.ElementName ?? GetType().Name;
                return _RootName;
            }
        }

        /// <summary>
        /// Obtient le nom des éléments.
        /// </summary>
        protected virtual string ItemName
        {
            get { return _ItemName; }
        }

        /// <summary>
        /// Cette méthode est réservée et ne doit pas être utilisée.Lorsque vous implémentez l'interface IXmlSerializable, vous devez retourner la valeur null (Nothing dans Visual Basic) à partir cette méthode et, si la spécification d'un schéma personnalisé est requise, appliquez à la place <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> à la classe.
        /// </summary>
        /// <returns> <see cref="T:System.Xml.Schema.XmlSchema"/> qui décrit la représentation XML de l'objet qui est généré par la méthode <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> et utilisé par la méthode <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/>.</returns>
        public XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Génère un objet à partir de sa représentation XML.
        /// </summary>
        /// <param name="reader"><see cref="T:System.Xml.XmlReader"/> source à partir de laquelle l'objet est désérialisé.</param>
        public void ReadXml(XmlReader reader)
        {
            if (!reader.IsEmptyElement)
            {
                reader.ReadStartElement();
                while (reader.NodeType != XmlNodeType.EndElement)
                {
                    T item = (T) _ItemSerializer.Deserialize(reader);
                    Add(item);
                }
                reader.ReadEndElement();
            }
            else reader.ReadStartElement();
        }

        /// <summary>
        /// Convertit un objet en sa représentation XML.
        /// </summary>
        /// <param name="writer"><see cref="T:System.Xml.XmlWriter"/> flux dans lequel l'objet est sérialisé.</param>
        public void WriteXml(XmlWriter writer)
        {
            foreach (var i in this)
            {
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                ns.Add("", "");
                _ItemSerializer.Serialize(writer, i, ns);
            }
        }
        #endregion
    }
}

И здесь класс unit test для демонстрации использования и результатов:

#region Références
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endregion

namespace XmlSerializationTests
{
    [TestClass]
    public class XmlSerializableListTests
    {
        public class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Birth { get; set; }
        }

        [XmlRoot("color")]
        public class ColorDefinition
        {
            [XmlElement("name")] public string Name { get; set; }
            [XmlElement("r")] public int Red { get; set; }
            [XmlElement("g")] public int Green { get; set; }
            [XmlElement("b")] public int Blue { get; set; }
        }

        public class Persons : XmlSerializableList<Person>
        {
        }

        [XmlRoot("colors")]
        public class ColorList : XmlSerializableList<ColorDefinition>
        {
        }

        private T ReadXml<T>(string text) where T : class
        {
            XmlSerializer serializer = new XmlSerializer(typeof (T));
            using (StringReader sr = new StringReader(text))
            {
                return serializer.Deserialize(sr) as T;
            }
        }

        private string WriteXml<T>(T data) where T : class
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            StringBuilder sb = new StringBuilder();
            using (StringWriter sw = new StringWriter(sb))
            {
                serializer.Serialize(sw, data);
                return sb.ToString();
            }
        }

        [TestMethod]
        public void ReadEmpty()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfInt32>
</XmlSerializableListOfInt32>";
            XmlSerializableList<int> lst = ReadXml<XmlSerializableList<int>>(xml);
            Assert.AreEqual(0, lst.Count);
        }

        [TestMethod]
        public void ReadEmpty2()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfInt32 />";
            XmlSerializableList<int> lst = ReadXml<XmlSerializableList<int>>(xml);
            Assert.AreEqual(0, lst.Count);
        }

        [TestMethod]
        public void ReadSimpleItems()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfInt32>
  <int>0</int>
  <int>52</int>
  <int>79</int>
</XmlSerializableListOfInt32>";
            XmlSerializableList<int> lst = ReadXml<XmlSerializableList<int>>(xml);
            Assert.AreEqual(3, lst.Count);
            Assert.AreEqual(0, lst[0]);
            Assert.AreEqual(52, lst[1]);
            Assert.AreEqual(79, lst[2]);
        }

        [TestMethod]
        public void ReadComplexItems()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfPerson>
  <Person>
    <FirstName>Linus</FirstName>
    <LastName>Torvalds</LastName>
    <Birth>1969</Birth>
  </Person>
  <Person>
    <FirstName>Bill</FirstName>
    <LastName>Gates</LastName>
    <Birth>1955</Birth>
  </Person>
  <Person>
    <FirstName>Steve</FirstName>
    <LastName>Jobs</LastName>
    <Birth>1955</Birth>
  </Person>
</XmlSerializableListOfPerson>";
            XmlSerializableList<Person> lst = ReadXml<XmlSerializableList<Person>>(xml);
            Assert.AreEqual(3, lst.Count);
            Assert.AreEqual("Linus", lst[0].FirstName);
            Assert.AreEqual("Torvalds", lst[0].LastName);
            Assert.AreEqual(1969, lst[0].Birth);
            Assert.AreEqual("Bill", lst[1].FirstName);
            Assert.AreEqual("Gates", lst[1].LastName);
            Assert.AreEqual(1955, lst[1].Birth);
            Assert.AreEqual("Steve", lst[2].FirstName);
            Assert.AreEqual("Jobs", lst[2].LastName);
            Assert.AreEqual(1955, lst[2].Birth);
        }

        [TestMethod]
        public void ReadInheritedPersons()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<Persons>
  <Person>
    <FirstName>Linus</FirstName>
    <LastName>Torvalds</LastName>
    <Birth>1969</Birth>
  </Person>
  <Person>
    <FirstName>Bill</FirstName>
    <LastName>Gates</LastName>
    <Birth>1955</Birth>
  </Person>
  <Person>
    <FirstName>Steve</FirstName>
    <LastName>Jobs</LastName>
    <Birth>1955</Birth>
  </Person>
</Persons>";
            Persons lst = ReadXml<Persons>(xml);
            Assert.AreEqual(3, lst.Count);
            Assert.AreEqual("Linus", lst[0].FirstName);
            Assert.AreEqual("Torvalds", lst[0].LastName);
            Assert.AreEqual(1969, lst[0].Birth);
            Assert.AreEqual("Bill", lst[1].FirstName);
            Assert.AreEqual("Gates", lst[1].LastName);
            Assert.AreEqual(1955, lst[1].Birth);
            Assert.AreEqual("Steve", lst[2].FirstName);
            Assert.AreEqual("Jobs", lst[2].LastName);
            Assert.AreEqual(1955, lst[2].Birth);
        }

        [TestMethod]
        public void ReadInheritedColors()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<colors>
  <color>
    <name>red</name>
    <r>255</r>
    <g>0</g>
    <b>0</b>
  </color>
  <color>
    <name>green</name>
    <r>0</r>
    <g>255</g>
    <b>0</b>
  </color>
  <color>
    <name>yellow</name>
    <r>255</r>
    <g>255</g>
    <b>0</b>
  </color>
</colors>";
            ColorList lst = ReadXml<ColorList>(xml);
            Assert.AreEqual(3, lst.Count);
            Assert.AreEqual("red", lst[0].Name);
            Assert.AreEqual(255, lst[0].Red);
            Assert.AreEqual(0, lst[0].Green);
            Assert.AreEqual(0, lst[0].Blue);
            Assert.AreEqual("green", lst[1].Name);
            Assert.AreEqual(0, lst[1].Red);
            Assert.AreEqual(255, lst[1].Green);
            Assert.AreEqual(0, lst[1].Blue);
            Assert.AreEqual("yellow", lst[2].Name);
            Assert.AreEqual(255, lst[2].Red);
            Assert.AreEqual(255, lst[2].Green);
            Assert.AreEqual(0, lst[2].Blue);
        }

        [TestMethod]
        public void WriteEmpty()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfInt32 />";
            XmlSerializableList<int> lst = new XmlSerializableList<int>();
            string result = WriteXml(lst);
            Assert.AreEqual(xml, result);
        }

        [TestMethod]
        public void WriteSimpleItems()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfInt32>
  <int>0</int>
  <int>52</int>
  <int>79</int>
</XmlSerializableListOfInt32>";
            XmlSerializableList<int> lst = new XmlSerializableList<int>() {0, 52, 79};
            string result = WriteXml(lst);
            Assert.AreEqual(xml, result);
        }

        [TestMethod]
        public void WriteComplexItems()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<XmlSerializableListOfPerson>
  <Person>
    <FirstName>Linus</FirstName>
    <LastName>Torvalds</LastName>
    <Birth>1969</Birth>
  </Person>
  <Person>
    <FirstName>Bill</FirstName>
    <LastName>Gates</LastName>
    <Birth>1955</Birth>
  </Person>
  <Person>
    <FirstName>Steve</FirstName>
    <LastName>Jobs</LastName>
    <Birth>1955</Birth>
  </Person>
</XmlSerializableListOfPerson>";
            XmlSerializableList<Person> persons = new XmlSerializableList<Person>
            {
                new Person {FirstName = "Linus", LastName = "Torvalds", Birth = 1969},
                new Person {FirstName = "Bill", LastName = "Gates", Birth = 1955},
                new Person {FirstName = "Steve", LastName = "Jobs", Birth = 1955}
            };
            string result = WriteXml(persons);
            Assert.AreEqual(xml, result);
        }

        [TestMethod]
        public void WriteInheritedPersons()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<Persons>
  <Person>
    <FirstName>Linus</FirstName>
    <LastName>Torvalds</LastName>
    <Birth>1969</Birth>
  </Person>
  <Person>
    <FirstName>Bill</FirstName>
    <LastName>Gates</LastName>
    <Birth>1955</Birth>
  </Person>
  <Person>
    <FirstName>Steve</FirstName>
    <LastName>Jobs</LastName>
    <Birth>1955</Birth>
  </Person>
</Persons>";
            Persons lst = new Persons
            {
                new Person {FirstName = "Linus", LastName = "Torvalds", Birth = 1969},
                new Person {FirstName = "Bill", LastName = "Gates", Birth = 1955},
                new Person {FirstName = "Steve", LastName = "Jobs", Birth = 1955}
            };
            string result = WriteXml(lst);
            Assert.AreEqual(xml, result);
        }

        [TestMethod]
        public void WriteInheritedColors()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<colors>
  <color>
    <name>red</name>
    <r>255</r>
    <g>0</g>
    <b>0</b>
  </color>
  <color>
    <name>green</name>
    <r>0</r>
    <g>255</g>
    <b>0</b>
  </color>
  <color>
    <name>yellow</name>
    <r>255</r>
    <g>255</g>
    <b>0</b>
  </color>
</colors>";
            ColorList lst = new ColorList
            {
                new ColorDefinition { Name = "red", Red = 255, Green = 0, Blue = 0 },
                new ColorDefinition { Name = "green", Red = 0, Green = 255, Blue = 0 },
                new ColorDefinition { Name = "yellow", Red = 255, Green = 255, Blue = 0 }
            };
            string result = WriteXml(lst);
            Assert.AreEqual(xml, result);
        }
    }
}