はじめに
XMLファイルの入出力を行う方法として、今回は下記3つの方法を使用したXMLファイルの入出力について解説します。
- System.Xml.Serialization.XmlSerializer
- System.Xml.XmlTextWriter/System.Xml.XmlTextReader
- System.Xml.Linq.XElement
System.Xml.Serialization.XmlSerializer
XMLシリアライズ、デシリアライズ関数
XMLSerializerを使用したXMLファイルの読込み、出力を行う関数を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
/// <summary> /// 指定のXMLファイルの内容を指定した型にデシリアライズします。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="path"></param> /// <returns></returns> public static T Load<T>(string path) { var serializer = new XmlSerializer(typeof(T)); using (var stream = File.OpenRead(path)) { return (T)serializer.Deserialize(stream); } } /// <summary> /// 指定のXMLファイルの内容を指定した型のコレクションにデシリアライズします。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="path"></param> /// <returns></returns> public static IEnumerable<T> LoadFromCollection<T>(string path) { var serializer = new XmlSerializer(typeof(T[])); using (var stream = File.OpenRead(path)) { return (T[])serializer.Deserialize(stream); } } /// <summary> /// 指定のオブジェクトをシリアライズして指定したXMLファイルに出力します。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source"></param> /// <param name="path"></param> public static void Save<T>(T source, string path) { var serializer = new XmlSerializer(typeof(T)); using (var stream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { serializer.Serialize(stream, source); } } /// <summary> /// 指定のオブジェクトをシリアライズして指定したXMLファイルに出力します。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source"></param> /// <param name="path"></param> public static void Save<T>(IEnumerable<T> source, string path) { var serializer = new XmlSerializer(typeof(T[])); using (var stream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { serializer.Serialize(stream, source as T[] ?? source.ToArray()); } } |
シリアライズするユーザー定義型
XmlSerializerを使用する場合、シリアライズする型を定義します。
今回は以下の様なクラスを定義しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class XmlSerializeTest { public string Name { get; set; } public int Age { get; set; } public DateTime BirthDay { get; set; } public override string ToString() { var str = ""; foreach(var p in GetType().GetProperties()) { str += $"{p.Name}:{p.GetValue(this)}\r\n"; } return str; } } |
XMLファイルのシリアライズ
XMLファイルをシリアライズしてファイルに出力を行うには以下の様に使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var path1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test1.xml"); // ユーザー定義型をXMLにシリアライズして出力 Save<XmlSerializeTest>( new XmlSerializeTest { Name = "太郎", Age = 31, BirthDay = new DateTime(1991, 1, 1) }, path1); var path2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test2.xml"); var list = new List<XmlSerializeTest> { new XmlSerializeTest { Name = "田中", Age = 31 , BirthDay = new DateTime(1991, 1, 1)}, new XmlSerializeTest { Name = "佐藤", Age = 12 , BirthDay = new DateTime(2010, 1, 1)}, new XmlSerializeTest { Name = "山田", Age = 32 , BirthDay = new DateTime(1990, 1, 1)}, new XmlSerializeTest { Name = "小林", Age = 55 , BirthDay = new DateTime(1968, 1, 1)}, new XmlSerializeTest { Name = "伊藤", Age = 43 , BirthDay = new DateTime(1979, 1, 1)}, new XmlSerializeTest { Name = "鈴木", Age = 78 , BirthDay = new DateTime(1944, 1, 1)}, }; // ユーザー定義型のコレクションをXMLにシリアライズして出力 Save(list, path2); |
以下が出力したファイルの内容です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// test1.xml <?xml version="1.0"?> <XmlSerializeTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Name>太郎</Name> <Age>31</Age> <BirthDay>1991-01-01T00:00:00</BirthDay> </XmlSerializeTest> // test2.xml <?xml version="1.0"?> <ArrayOfXmlSerializeTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <XmlSerializeTest> <Name>田中</Name> <Age>31</Age> <BirthDay>1991-01-01T00:00:00</BirthDay> </XmlSerializeTest> <XmlSerializeTest> <Name>佐藤</Name> <Age>12</Age> <BirthDay>2010-01-01T00:00:00</BirthDay> </XmlSerializeTest> <XmlSerializeTest> <Name>山田</Name> <Age>32</Age> <BirthDay>1990-01-01T00:00:00</BirthDay> </XmlSerializeTest> <XmlSerializeTest> <Name>小林</Name> <Age>55</Age> <BirthDay>1968-01-01T00:00:00</BirthDay> </XmlSerializeTest> <XmlSerializeTest> <Name>伊藤</Name> <Age>43</Age> <BirthDay>1979-01-01T00:00:00</BirthDay> </XmlSerializeTest> <XmlSerializeTest> <Name>鈴木</Name> <Age>78</Age> <BirthDay>1944-01-01T00:00:00</BirthDay> </XmlSerializeTest> </ArrayOfXmlSerializeTest> |
XMLファイルのデシリアライズ
XMLファイルのシリアライズで出力した内容をデシリアライズしてユーザー定義型にマッピングするには、以下の様にします。
1 2 3 4 5 6 7 8 9 10 |
// XMLを読込んでユーザー定義型にマッピング var test1 = Load<XmlSerializeTest>(path1); Console.WriteLine(test1); // XMLを読込んでユーザー定義型のコレクションにマッピング var test2 = LoadFromCollection<XmlSerializeTest>(path2); foreach (var obj in test2) { Console.WriteLine(obj); } |
以下の様に正常にデシリアライズされている事が確認出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// test1 Name:太郎 Age:31 BirthDay:1991/01/01 0:00:00 // test2 Name:田中 Age:31 BirthDay:1991/01/01 0:00:00 Name:佐藤 Age:12 BirthDay:2010/01/01 0:00:00 Name:山田 Age:32 BirthDay:1990/01/01 0:00:00 Name:小林 Age:55 BirthDay:1968/01/01 0:00:00 Name:伊藤 Age:43 BirthDay:1979/01/01 0:00:00 Name:鈴木 Age:78 BirthDay:1944/01/01 0:00:00 |
System.Xml.XmlTextWriter/System.Xml.XmlTextReader
XmlTextWriteでXMLファイル出力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.xml"); using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write)) using (var xr = new XmlTextWriter(fs, Encoding.UTF8)) { // 書式設定 xr.Formatting = Formatting.Indented; // インデントの量 xr.Indentation = 1; // インデント文字 xr.IndentChar = '\t'; // XMLドキュメント出力開始 xr.WriteStartDocument(true); // コメント出力 xr.WriteComment("XmlTextWriteのテストです。"); // Rootノード開始 xr.WriteStartElement("Root"); xr.WriteAttributeString("att", "属性"); // firstノード開始 xr.WriteStartElement("string"); // firstノードの値を出力 xr.WriteString("firstノードの値です"); xr.WriteString("連続して出力した場合、1度目の出力の後ろに続けて出力されます。"); // firstノード終了 xr.WriteEndElement(); // secondノード開始 xr.WriteStartElement("second"); // secondノードの値を出力 xr.WriteValue(DateTime.Now); // secondノード終了 xr.WriteEndElement(); // Rootノード終了 xr.WriteEndElement(); // XMLドキュメント出力終了 xr.WriteEndDocument(); xr.Flush(); } |
- Formattingで書式を設定します。
インデントの有無を指定出来ます。 - IndentCharではインデント文字を指定します。
ここではタブを指定しています。 - Indentationではインデントの数を指定します。
1を指定しているので、タブ文字が1つインデントに使用されます。
スペース等、用途に合わせて指定します。 - WriteStartDocument
XML宣言を出力します。<?xml version=”1.0″ ~~~?>というやつです。 - WriteComment
XMLにコメントを出力します。
<!– {コメント} –>の様に出力されます。 - WriteStartElement
開始要素を出力します。 - WriteAttributeString
要素の属性を出力します。
引数には”属性名”,”値”を指定します。 - WriteString,WriteValue
要素の値を出力します。 - WriteEndElement
最後に出力した開始要素の終了要素を出力します。 - WriteEndDocument
開始している要素、属性を閉じてXmlTextWriterを開始状態に戻します。
以下が出力したXMLファイルです。
1 2 3 4 5 6 |
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <!--XmlTextWriteのテストです。--> <Root att="属性"> <string>stringノードの値です連続して出力した場合、1度目の出力の後ろに続けて出力されます。</string> <object>2022-04-17T22:58:43.6229631+09:00</object> </Root> |
XmlTextReaderでXMLファイル読込み
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.xml"); using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) using (var xr = new XmlTextReader(fs)) { while(xr.Read()) { Console.WriteLine($"NodeType:{xr.NodeType}"); Console.WriteLine($"\tName:{xr.Name}"); Console.WriteLine($"\tValue:{xr.Value}"); if (xr.HasAttributes) { for (var i = 0; i < xr.AttributeCount; i++) { Console.WriteLine($"\tAttribute:{xr.GetAttribute(i)}"); } } } } |
以下がXmlTextWriteで出力したXMLファイルの読込み結果です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
NodeType:XmlDeclaration Name:xml Value:version="1.0" encoding="utf-8" standalone="yes" Attribute:1.0 Attribute:utf-8 Attribute:yes NodeType:Whitespace Name: Value: NodeType:Comment Name: Value:XmlTextWriteのテストです。 NodeType:Whitespace Name: Value: NodeType:Element Name:Root Value: Attribute:属性 NodeType:Whitespace Name: Value: NodeType:Element Name:first Value: NodeType:Text Name: Value:firstノードの値です連続して出力した場合、1度目の出力の後ろに続けて出力されます。 NodeType:EndElement Name:first Value: NodeType:Whitespace Name: Value: NodeType:Element Name:second Value: NodeType:Text Name: Value:2022-04-17T23:25:05.0684649+09:00 NodeType:EndElement Name:second Value: NodeType:Whitespace Name: Value: NodeType:EndElement Name:Root Value: |
- Read
ノードを1つずつ読込みます。 - NodeType
要素の種類を判別します。 - Name
要素名を取得します。 - Value
要素から値を取得します。 - GetAttribute
要素から属性を取得します。
取得する属性のインデックス又は属性名を引数に指定します。
値は文字列で取得されるので、適宜パースする必要があり、正直使いにくいです。
System.Xml.Linq.XElement
System.Xml.Linq.XElementでXMLファイル出力
System.Xml.Linq.XElementを使用してXMLファイルを出力するには以下の様に使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.xml"); var xml = new XElement("人造人間リスト"); foreach(var i in Enumerable.Range(0, 10)) { var node = new XElement("人造人間"); node.Add(new XElement("name", $"人造人間{i}号")); node.Add(new XElement("power", $"{i * 100}")); node.Add(new XElement("defense", $"{i * 50}")); xml.Add(node); } xml.Save(path); |
以下が出力したXMLファイルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?xml version="1.0" encoding="utf-8"?> <人造人間リスト> <人造人間> <name>人造人間0号</name> <power>0</power> <defense>0</defense> </人造人間> <人造人間> <name>人造人間1号</name> <power>100</power> <defense>50</defense> </人造人間> <人造人間> <name>人造人間2号</name> <power>200</power> <defense>100</defense> </人造人間> <人造人間> <name>人造人間3号</name> <power>300</power> <defense>150</defense> </人造人間> <人造人間> <name>人造人間4号</name> <power>400</power> <defense>200</defense> </人造人間> <人造人間> <name>人造人間5号</name> <power>500</power> <defense>250</defense> </人造人間> <人造人間> <name>人造人間6号</name> <power>600</power> <defense>300</defense> </人造人間> <人造人間> <name>人造人間7号</name> <power>700</power> <defense>350</defense> </人造人間> <人造人間> <name>人造人間8号</name> <power>800</power> <defense>400</defense> </人造人間> <人造人間> <name>人造人間9号</name> <power>900</power> <defense>450</defense> </人造人間> </人造人間リスト> |
XElementが1つのノードを表しています。
要素に子要素を追加する場合はAddでXElemenを追加すればOKです。
属性を指定する場合は、SetAttributeValueを使用します。
System.Xml.Linq.XElementでXMLファイル読込み
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.xml"); // XMLを読込む var xml = XElement.Load(path); // 要素名が"人造人間"の要素を抽出する foreach (var elem in xml.Elements("人造人間")) { Console.WriteLine(elem.Name); foreach (var child in elem.Elements()) { Console.WriteLine("\t" + child.Name + ":" + child.Value); } } // Linqで条件を指定して要素を抽出する var maxpower = xml.Elements("人造人間").Max(e => e.Element("power").Value); var strongest = xml.Elements("人造人間").Where(e=>e.Element("power").Value == maxpower).FirstOrDefault(); Console.WriteLine("最強の人造人間は" + strongest.Element("name").Value); |
以下が読込み結果です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
人造人間 name:人造人間0号 power:0 defense:0 人造人間 name:人造人間1号 power:100 defense:50 人造人間 name:人造人間2号 power:200 defense:100 人造人間 name:人造人間3号 power:300 defense:150 人造人間 name:人造人間4号 power:400 defense:200 人造人間 name:人造人間5号 power:500 defense:250 人造人間 name:人造人間6号 power:600 defense:300 人造人間 name:人造人間7号 power:700 defense:350 人造人間 name:人造人間8号 power:800 defense:400 人造人間 name:人造人間9号 power:900 defense:450 最強の人造人間は人造人間9号 |
まとめ
ごりへいの使い分けとしては、
ユーザー定義型をシリアライズ、デシリアライズする場合はSystem.Xml.Serialization.XmlSerializerを使用。
XMLファイルやXML文字列の解析等はSystem.Xml.Linq.XElementを使用する感じです。
(System.Xml.Linq.XElementでもユーザー定義型のシリアライズ、デシリアライズは出来ます。)
System.Xml.XmlTextWriter/System.Xml.XmlTextReaderは正直使いにくすぎるため、業務で使用した事が1度もありません。
コメント