.NET(C#)で、設定情報をXMLで管理するための実装方法を試してみた。
####1.設定情報のxmlファイルを作成
【CompanyInfo.xml】
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns="http://schemas.datacontract.org/2004/07/XMLDataContract">
<company>ほんにゃら産業</company>
<departments>
<department>
<name>営業部</name>
<employees>
<employee>
<name>かりあげくん</name>
<gender>男</gender>
</employee>
<employee>
<name>木村課長</name>
<gender>男</gender>
</employee>
<employee>
<name>鈴木</name>
<gender>男</gender>
</employee>
<employee>
<name>ヒサエさん</name>
<gender>女</gender>
</employee>
</employees>
</department>
<department>
<name>総務部</name>
<employees>
<employee>
<name>佐藤クン</name>
<gender>男</gender>
</employee>
<employee>
<name>山田さん</name>
<gender>女</gender>
</employee>
<employee>
<name>太子さん</name>
<gender>女</gender>
</employee>
</employees>
</department>
</departments>
</root>
- プロパティの「出力ディレクトリにコピー」は「コピーをしない」がデフォルトだが、そのままだとファイルがない旨のエラーが発生するので、それ以外に設定する。
- xmlns属性を追加しないとエラーになる。プロジェクトからxmlファイルを追加する場合は、自動的に挿入されないので手入力する必要がある。
<root xmlns="http://schemas.datacontract.org/2004/07/XMLDataContract" >
<!-- xmlns="http://schemas.datacontract.org/2004/07/名前空間名 -->
2.デシリアライズのためのクラスを作成
- 参照設定に「System.Runtime.Serialization」を追加する。
- デシリアライズさせるためのクラスを定義する。まずは会社名(company)だけをやってみる。
【CompanyInfo.cs】
using System.Runtime.Serialization;
namespace XMLDataContract {
[DataContract(Name = "root")]
public class CompanyInfo {
[DataMember(Name = "company")]
public string Company {
get;private set;
}
}
}
3.デシリアライズのメソッドを作成
- 汎用的に利用するメソッドを作成
【XMLDataContract.cs】
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
namespace XMLDataContract {
public class XMLDataContract {
public static T Load<T>(Stream stream) where T : new() {
T result = default(T);
var serialiser = new DataContractSerializer(typeof(T));
using(var reader = XmlReader.Create(stream)) {
result = (T)serialiser.ReadObject(reader);
}
return result;
}
}
}
単体テストのコード
using Microsoft.VisualStudio.TestTools.UnitTesting;
using XMLDataContract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace XMLDataContract.Tests {
[TestClass()]
public class XMLDataContractTests {
[TestMethod()]
public void LoadTest() {
//Arrange
string filename = "CompanyInfo.xml";
using(Stream filestream = File.Open(filename, FileMode.Open)) {
//Act
var deserialize = XMLDataContract.Load<CompanyInfo>(filestream);
//Assert
Assert.AreEqual(deserialize.Company, "ほんにゃら産業");
}
}
}
}
実際にテストを実行すると問題なければ、その他の属性もデシリアライズさせてみる。
4.その他の属性をデシリアライズ
- CompanyInfo.xmlは部門(department)が配列(departments)になっていて、更に従業員(employee)が配列(employees)になっている。これらをデシリアライズさせるには「CollectionDataContractAttribute」を使用する。先程作成した「CompanyInfo.cs」に追記する。
using System.Runtime.Serialization;
using System.Collections.Generic;
namespace XMLDataContract {
[DataContract(Name = "root")]
public class CompanyInfo {
[DataMember(Name = "company")]
public string Company {
get;private set;
}
[DataMember(Name = "departments")]
public Departments Departments {
get;private set;
}
}
[CollectionDataContract(Name = "departments",ItemName = "department")]
public class Departments : List<Department> {
}
[DataContract(Name = "department")]
public class Department {
[DataMember(Name = "name")]
public string DeparmentName {
get;private set;
}
}
}
単体テスト
[TestMethod()]
public void LoadTest2() {
//Arrange
string filename = "CompanyInfo.xml";
using(Stream filestream = File.Open(filename, FileMode.Open)) {
//Act
var deserialize = XMLDataContract.Load<CompanyInfo>(filestream);
//Assert
Assert.AreEqual(deserialize.Departments[1].DeparmentName, "総務部");
}
}
5.最終型の実装
using System.Runtime.Serialization;
using System.Collections.Generic;
namespace XMLDataContract {
[DataContract(Name = "root")]
public class CompanyInfo {
[DataMember(Name = "company")]
public string Company {
get;private set;
}
[DataMember(Name = "departments")]
public Departments Departments {
get;private set;
}
}
[CollectionDataContract(Name = "departments",ItemName = "department")]
public class Departments : List<Department> {
}
[DataContract(Name = "department")]
public class Department {
[DataMember(Name = "name",Order = 1)]
public string DeparmentName {
get;private set;
}
[DataMember(Name = "employees",Order = 2)]
public Employees Employees {
get; private set;
}
}
[CollectionDataContract(Name = "employees", ItemName = "employee")]
public class Employees : List<Emplyee> {
}
[DataContract(Name = "employee")]
public class Emplyee {
[DataMember(Name = "name",Order = 1)]
public string EmployeeName {
get;private set;
}
[DataMember(Name = "gender",Order = 2)]
public string Gender {
get;private set;
}
}
}
ここでの注意点として、CollectionDataContractAttributeのItemNameに設定しているクラス内のDataMamberAttributeに「Order=」を設定していないとデシリアライズされない。ここで結構悩みました。
単体テスト
[TestMethod()]
public void LoadTest3() {
//Arrange
string filename = "CompanyInfo.xml";
using(Stream filestream = File.Open(filename, FileMode.Open)) {
//Act
var deserialize = XMLDataContract.Load<CompanyInfo>(filestream);
//Assert
Assert.AreEqual(deserialize.Departments[0].Employees[1].EmployeeName, "木村課長");
Assert.AreEqual(deserialize.Departments[0].Employees[1].Gender, "男");
}
}
以上です。