我有一个班级PersonList
[XmlRoot("Persons")]
PersonList : List<Human>
当我将其序列化为XML时,默认情况下会生成如下内容:
<Persons>
<Human>...</Human>
<Human>...</Human>
</Persons>
我的问题是,为了在输出中将元素Human
更改为Person
,需要做些什么?所以输出将是:
<Persons>
<Person>...</Person>
<Person>...</Person>
</Persons>
以及如何将上述XML反序列化为PersonList
类对象?
Per Nick的建议,这是我的测试代码:
[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
。
答案 0 :(得分:54)
使用以下属性标记您的课程:
[XmlType("Account")]
[XmlRoot("Account")]
答案 1 :(得分:28)
我认为没有办法控制生成的数组元素的名称。
如果您可以将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 :(得分:13)
我的序列化程序遇到了同样的问题。上述答案都没有完全奏效。我发现人类的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类添加属性的奖励选项,这是我从中获得的想法:
How do I add a attribute to a XmlArray element (XML Serialization)?
答案 3 :(得分: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>
答案 4 :(得分:3)
将人类XmlRoot
设置为:
[XmlRoot("Person")]
<强>侧栏:强>
人应该是人
答案 5 :(得分:2)
还有另一种选择。您始终可以在集合(以及任何其他类或结构)中实现IXmlSerializable,以完全控制项的写入或读取方式。你们当中很多人都知道,当然不是一般的首选,因为你可能最终用手写出“锅炉板”代码,这应该是属性指定的自动逻辑。
对于必须与合理架构匹配的集合,这是合理的。因为框架在这里有一个硬限制,并且在正确完成时不必复制现有项类型的序列化代码;即,不要在集合代码中重写项目序列化,只需在ReadXml / WriteXml实现中创建/调用子XmlSerializer。
使用IXmlSerializable的后果是它不允许您应用XmlTypeAttribute(抛出运行时错误,告诉您只能使用XmlRootAttribute)。因此,应用XmlSchemaProviderAttribute并返回您在XmlTypeAttribute中放置的相同限定名称。旧的GetSchema方法应该返回null,因为它只是一个保留的方法(根据MSDN),可能是因为它们忘记了包含指定不同命名空间的能力。我个人在XmlSchemaProviderAttribute中使用相同的“GetSchema”方法名称,因此它在旧占位符GetSchema方法旁边显示为完整覆盖。
当然,最好的解决方案是,如果Microsoft允许我们将XmlArrayItemAttribute应用于集合/列表类并在XmlSerializer中使用它。默认情况下,它使用集合中的XML类型元素名称,我认为这是一个错误,因为它应该是指定时的XML根名称或不是时的类名。
当我有时间时,我会回来再添一个例子。现在看一下IXmlSerializable和XmlSchemaProviderAttribute的MSDN文档示例。
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable(v=vs.110).aspx
答案 6 :(得分:1)
如果您无法访问Human类的源代码(在这种情况下,无法设置XmlRoot),您可以创建一个XmlElementAttribute,然后将其添加到XmlAttributeOverride并在创建一个实例时使用它XmlSerializer的。 See this MSDN article for more details
答案 7 :(得分: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);
}
}
}