Xml ファイルの読み書きをするとき、
- List( リスト ) を保存したい
- List をそのまま読み込みたい
ということはよくあります。
そこで、本記事では、リストの読み書きの方法を書きます。
XMLファイルの読み書きに、XmlSerializer を使用します。クラスオブジェクトをXMLファイルに相互変換するイメージで使うことができます。
XMLファイルと対応するファーストクラスコレクション
以下の構成を例にします。UsersXmlクラスがUserXmlクラスオブジェクトのListをメンバとして持っています。
UsersXmlクラスはファーストクラスコレクションとなっています。
著者は、プログラミング言語で提供されているリストやマップなどのコレクションを
ThoughtWorksアンソロジー P77 5.2.8 ルール7 : 1つのクラスにつきインスタンス変数は2つまでにすること
プリミティブと見なして、それをラップしたクラスをファーストクラスコレクションと呼ん
でいる。
UsersXmlは、UserXmlのリストをラップしたファーストクラスコレクションです。
namespace XmlProject
{
public class UsersXml
{
public List<UserXml> userList;
public UsersXml()
{
userList = new List<UserXml>();
}
}
}
それ以外のクラスは↓のようになります。
UserXml.cs のコード
using System.Xml.Serialization;
namespace XmlProject
{
/// <summary>
/// ユーザーXml
/// </summary>
[XmlInclude(typeof(AdministratorXml))]
[XmlInclude(typeof(EditorXml))]
public abstract class UserXml
{
}
}
AdministratorXml.cs のコード
namespace XmlProject
{
/// <summary>
/// 管理者Xml
/// </summary>
public class AdministratorXml : UserXml
{
/// <summary>ID</summary>
public string? id;
/// <summary>パスワード</summary>
public string pass;
/// <summary>年齢</summary>
public int age;
/// <summary>年齢表示判定</summary>
public bool isViewAge;
/// <summary>役職</summary>
public string position;
public AdministratorXml()
{
id = null;
pass = "abc";
age = 30;
isViewAge = false;
position = "未設定";
}
}
}
EditorXml.csのコード
namespace XmlProject
{
/// <summary>
/// 編集者Xml
/// </summary>
public class EditorXml : UserXml
{
/// <summary>ID</summary>
public string? id;
/// <summary>パスワード</summary>
public string pass;
/// <summary>画像編集許可判定</summary>
public bool isEnabledEditPicture;
public EditorXml() {
id = null;
pass = "abc";
isEnabledEditPicture = false;
}
}
}
基底クラスは、XmlIncludeを設定する必要があります。
XMLファイルに保存
XMLファイル保存クラス
XmlSerializerを使用した、Xml保存クラス であるXmlSaver は↓のようになります。
XmlSaver のコード
using System.Text;
using System.Xml.Serialization;
namespace XmlProject
{
/// <summary>
/// Xmlセーバー
/// </summary>
public class XmlSaver
{
/// <summary>
/// XMLファイルに保存する
/// </summary>
/// <typeparam name="T">型</typeparam>
/// <param name="xml">Xmlとして保存するクラス</param>
/// <param name="filePath">保存するファイルパス</param>
public static void Save<T>(T xml, string filePath)
{
//XmlSerializerオブジェクトを作成
XmlSerializer serializer = new XmlSerializer(typeof(T));
//書き込むファイルを開く(UTF-8 BOM無し)
using (StreamWriter sw = new StreamWriter(filePath, false, new UTF8Encoding(false)))
{
//シリアル化し、XMLファイルに保存する
serializer.Serialize(sw, xml);
}
}
}
}
XMLファイル保存実行
UsersXmlクラスオブジェクトをXMLファイルに保存します。AdministratorXml 1つとEditorXml 2つを保存しています。
using XmlProject;
UsersXml users = new UsersXml
{
userList = new List<UserXml>()
{
{ new AdministratorXml(){ id = "001", pass="ddd", age = 40 } },
{ new EditorXml() { id = "002", pass="eee", isEnabledEditPicture = true } },
{ new EditorXml() { id = "003", pass="fff", isEnabledEditPicture = false } }
}
};
string filePath = Environment.CurrentDirectory + "/Users.xml";
XmlSaver.Save(users, filePath);
コードを実行することで、XMLファイルに正しく保存されていることが確認できました。
XMLファイルの読み込み
次は、先ほど保存したXMLファイルを読み込んでみます。
XMLファイル読み込みクラス
XmlSerializerを使用してXMLファイルを読み込むXmlLoader クラスは↓のようになります。
XmlLoaderのコード
using System.Text;
using System.Xml.Serialization;
namespace XmlProject
{
/// <summary>
/// Xmlローダー
/// </summary>
internal class XmlLoader
{
// XMLファイルをロードする
public static T Load<T>(string path)
{
//XmlSerializerオブジェクトを作成
XmlSerializer serializer = new XmlSerializer(typeof(T));
//読み込むファイルを開く
using (StreamReader sr = new StreamReader(path, new UTF8Encoding(false)))
{
//XMLファイルから読み込み、逆シリアル化する
T t = (T?)serializer.Deserialize(sr) ?? throw new Exception();
return t;
}
}
}
}
XMLファイルの読み込み実行
読み込み実行します。
using XmlProject;
string filePath = Environment.CurrentDirectory + "/Users.xml";
UsersXml usersXml = XmlLoader.Load<UsersXml>(filePath);
デバッグでusersXmlを確認したところ、想定通りの値を読み込むことができました。
List→配列に変更しても同じ結果になる
先ほどまでは、UsersXmlのuserListをListクラスで保持していました。これを配列で保持する方法に変更した場合はどうなるのか検証します。
namespace XmlProject
{
public class UsersXml
{
public UserXml[] userList; // List→Array
public UsersXml()
{
userList = new UserXml[] { };
}
}
}
以下のコードで検証します。
using XmlProject;
UsersXml users = new UsersXml
{
userList = new UserXml[]
{
new AdministratorXml(){ id = "001", pass="ddd", age = 40 },
new EditorXml() { id = "002", pass="eee", isEnabledEditPicture = true },
new EditorXml() { id = "003", pass="fff", isEnabledEditPicture = false }
}
};
string filePath = Environment.CurrentDirectory + "/Users2.xml";
XmlSaver.Save(users, filePath);
UsersXml usersXml = XmlLoader.Load<UsersXml>(filePath);
結果、Xmlのファイルは、Listと配列で全く同じものになりました。
XmlLoader.Loadを使用すれば、XMLファイルの読み込みもできます。
Listを直接保存することはできるのか?
今までは、ファーストクラスコレクションであるUsersXmlを保存させていましたが、List<UserXml> の型を直接できるのでしょうか?
以下のコードで試してみます。XmlSaver.Saveの引数に直接リストを設定します。
using XmlProject;
List<UserXml> userList = new List<UserXml>()
{
{ new AdministratorXml(){ id = "001", pass="ddd", age = 40 } },
{ new EditorXml() { id = "002", pass="eee", isEnabledEditPicture = true } },
{ new EditorXml() { id = "003", pass="fff", isEnabledEditPicture = false } }
};
string filePath = Environment.CurrentDirectory + "/UserList.xml";
XmlSaver.Save(userList, filePath);
XMLファイルは以下のようになりました。
保存はできました。
しかし、2行目のクラス名が「ArrayOfUserXml」となっています。XmlSerializer が独自に定義した名前と思われます
Listを直接保存する方法は以下の観点から使用しないほうがいいと思います。クラス名とXMLファイルのタグの対応が分かりにくいからです。
手間が増えますが、ファーストクラスコレクションを使用したほうが、メンテナンス性という観点からよいと思います。
XMLファイルの読み込みは今までと同じ方法でできます。XmlLoader.Load のジェネリクスにList<UserXml>を指定します。
まとめ
C# でXml ファイルのリストの読み書きの方法を書きました。
XmlSerializerを使用すれば、リスト型のオブジェクトもXMLファイルと対応できます。
コメント