40

我有一堂课PersonList

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

当我将其序列化为 XML 时,默认情况下它将产生如下内容:

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

我的问题是需要做什么才能将元素更改HumanPerson输出?所以输出将是:

<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")]onHuman不会将标签更改为Personfrom Human

4

8 回答 8

61

使用以下属性标记您的班级:

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

XmlType 属性将导致 OP 中请求的输出。根据文档

控制 XmlSerializer 序列化属性目标时生成的 XML 架构

于 2011-10-18T19:51:54.910 回答
40

我认为您没有办法控制生成的数组元素的名称。

但是,如果您可以将集合包装在另一个类中,您将可以使用andPersons完全控制生成的输出。XmlArrayAttributeXmlArrayItemAttribute

如果你不能创建这个新类,你可以求助于实现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());
    }
}
于 2010-02-19T18:42:23.427 回答
16

我的序列化程序遇到了同样的问题。上面的答案都没有完全奏效。我发现 Human 类的 XmlRoot 属性显然被忽略了,因为它不是文档的根元素。将列表包装在上下文对象中对我来说不是一个选项,因为我无法更改 XML 模式。解决方案是更改 Persons 类。不是继承一个通用列表,而是将它包装在一个对象中并更改它的序列化方式。请参阅下面的示例代码:

[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 所做的那样或像子类化所做的那样将包装元素放在您的列表周围。它还为您提供了将属性添加到 Persons 类的额外选项,这就是我的想法:

如何将属性添加到 XmlArray 元素(XML 序列化)?

于 2010-04-07T22:05:53.583 回答
4

这是我的测试代码

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

namespace TestLoadingMultiXml
{
[XmlRoot(ElementName=@"main")]
public class XmlMain
{
    private XmlDataTest data;

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

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

[XmlRoot(ElementName=@"xmldata")]
public class XmlDataTest
{
    private List<DataDetails> listData;

    [XmlElement(ElementName=@"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(ElementName=@"datadetail")]
public class DataDetails
{
    private string name;

    [XmlAttribute(AttributeName=@"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>
于 2012-07-10T02:54:52.723 回答
2

XmlRoot人类设置为:

[XmlRoot("Person")]

侧边栏:

应该是

于 2010-02-18T21:56:14.230 回答
2

还有另一种选择。您始终可以在集合(以及任何其他类或结构)中实现 IXmlSerializable 以完全控制项目的写入或读取方式。你们中的许多人已经知道,这当然不是一般首选,因为您最终可能会手动编写“样板”代码,这实际上应该是使用属性指定的自动逻辑。

对于必须匹配合理模式的集合,这是合理的。因为框架在这里有一个硬性限制,并且现有项目类型的序列化代码在正确完成时不必复制;即不要在集合代码中重新编写项目序列化,只需在 ReadXml/WriteXml 实现中创建/调用子 XmlSerializer。

一旦使用 IXmlSerializable 的后果是它不允许您应用 XmlTypeAttribute(抛出运行时错误,告诉您只能使用 XmlRootAttribute)。因此,改为应用 XmlSchemaProviderAttribute 并返回您在 XmlTypeAttribute 中放入的相同限定名称。旧的 GetSchema 方法无论如何都应该返回 null,因为它只是一个保留方法(根据 MSDN),可能是因为他们忘记了包含指定不同命名空间的能力。我个人在我的 XmlSchemaProviderAttribute 中使用相同的“GetSchema”方法名称,因此它在旧占位符 GetSchema 方法旁边显示为一个完整的覆盖。

当然,最好的解决方案是微软允许我们将 XmlArrayItemAttribute 应用于集合/列表类并在 XmlSerializer 中使用它。默认情况下,它使用集合中的 XML 类型元素名称,我觉得这是一个错误,因为指定时它应该是 XML 根名称,否则它应该是类名。

当我有时间时,我会回来添加一个例子。现在看一下 IXmlSerializable 和 XmlSchemaProviderAttribute 的 MSDN 文档示例。

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

于 2014-05-28T11:32:52.473 回答
1

如果您无权访问 Human 类的源(在这种情况下,无法设置 XmlRoot),则可以创建一个 XmlElementAttribute,然后将其添加到 XmlAttributeOverride 并在创建 XmlSerializer 的实例时使用它。 有关详细信息,请参阅此 MSDN 文章

于 2010-02-18T22:02:55.090 回答
0

我知道这是一个老问题,但我遇到了同样的问题,似乎没有一个解决方案可以解决 OP 的问题。所以这是我的解决方案(如果你想知道的话,评论是法语):

#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
    }
}

这里有一个单元测试类来演示使用和结果:

#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);
        }
    }
}
于 2016-10-07T13:36:33.840 回答