クラス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>
_
出力が示すように、Human
の[XmlRoot("Person")]
は、タグをPerson
からHuman
に変更しません。
生成された配列要素の名前を制御する方法はないと思います。
ただし、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());
}
}
次の属性でクラスをマークします。
[XmlType("Account")]
[XmlRoot("Account")]
シリアライザーでも同じ問題が発生しました。上記の答えはどれも正確に機能しませんでした。 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クラスに属性を追加するボーナスオプションも提供します。これは、私がアイデアを得た場所です。
これは私のテストコードです
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>
HumanのXmlRoot
を次のように設定します。
[XmlRoot("Person")]
サイドバー:
PersonsはおそらくPeople
別の方法があります。コレクション(および他のクラスまたは構造体)にIXmlSerializableを常に実装して、アイテムの書き込みまたは読み取り方法を完全に制御できます。すでに多くの人がすでに知っていることですが、当然、属性で指定された自動ロジックである必要がある「ボイラープレート」コードを手で書くことになるため、一般的には好まれません。
賢明なスキーマに一致する必要があるコレクションの場合、それは正当化されます。ここにはフレームワークに厳しい制限があり、既存の項目タイプのシリアル化コードを適切に複製する必要がないためです。つまり、コレクションコードでアイテムのシリアル化を書き換えず、ReadXml/WriteXml実装内で子のXmlSerializerを作成/呼び出します。
IXmlSerializableを使用した結果、XmlTypeAttributeを適用できなくなります(XmlRootAttributeのみを使用できることを示すランタイムエラーがスローされます)。したがって、代わりにXmlSchemaProviderAttributeを適用し、XmlTypeAttributeに入れたのと同じ修飾名を返します。おそらく別の名前空間を指定する機能を含めるのを忘れたため、古いGetSchemaメソッドは予約メソッド(MSDNによる)のみであったため、とにかくnullを返す必要があります。個人的には、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
Humanクラスのソースにアクセスできない場合(この場合、XmlRootを設定することはできません)、XmlElementAttributeを作成し、それをXmlAttributeOverrideに追加して、XmlSerializerのインスタンスを作成するときに使用できます。 詳細については、このMSDN記事を参照 。
私はそれが古い質問であることを知っていますが、私は同じ問題にぶつかりました、そして、解決策のどれも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);
}
}
}