1. はじめに
XML (eXtensible Markup Language) — 拡張可能なマークアップ言語で、複雑な構造化データの保存や交換によく使われるフォーマット。JSONと比べると簡潔さには欠けるけど、銀行系や政府系サービス、ドキュメント、1С、SAP、Oracle などとの連携で今でも広く使われている。Windows や .NET の一部のデータ(例:設定ファイル)がまだ XML で保存されていることもある。
見た目は HTML に似ているけど、<body> や <div> のような固定タグはない — 自分で要素の構造を定義する。例:
<Person>
<FirstName>Ivan</FirstName>
<LastName>Petrov</LastName>
<Age>30</Age>
</Person>
XMLの基礎と C# オブジェクトとの対応
JSONと違って、XML には配列型や boolean 型みたいな専用の型があるわけじゃない — すべて要素や属性で表現される。リスト構造は同一名の要素を繰り返すことで実現する。
C# で XML を扱うときは XmlSerializer を使ったシリアライズ/デシリアライズの仕組みが便利。
シリアライズ — C# オブジェクトを XML 文字列に変換すること。デシリアライズ — その逆で、XML から C# オブジェクトを作ること。
いくつか条件を満たしていれば “箱から出してすぐ使える” 感じで動く。以下で詳しく見るよ。
なぜ XmlSerializer を使うのか?
XmlSerializer — .NET の標準ツール(名前空間 System.Xml.Serialization)で、オブジェクトと XML の相互変換を行う。シンプルで型安全、データのインポート/エクスポート、設定、システム間のデータ連携や政府系サービスとの統合によく使える。
2. 例:オブジェクトのシリアライズ
ユーザーモデルを用意する:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
オブジェクトを XML にシリアライズする:
using System.Xml.Serialization;
User user = new User { FirstName = "イワン", LastName = "ペトロフ", Age = 30 };
// シリアライズには型が必要
XmlSerializer serializer = new XmlSerializer(typeof(User));
// XML をファイルに書き込む
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);
何が起きているか?
- クラス User のインスタンスを作る。
- 型 User 用の XmlSerializer を作る。
- FileStream でファイルを開く。
- Serialize() を呼んで、オブジェクトを XML に変換してファイルに書き込む。
ファイル user.xml の中身:
<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>イワン</FirstName>
<LastName>ペトロフ</LastName>
<Age>30</Age>
</User>
こんな感じで簡単 — 最低限のコードで XML ができる。
3. デシリアライズ:XML をオブジェクトに戻す
作成した XML を読み込んで C# オブジェクトに戻す:
// XML ファイルを開く
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, 年齢: {loadedUser?.Age}");
メソッド Deserialize() は object を返すので User にキャスト(ここでは as を使用)している。ファイルがモデルに合っていればオブジェクトは完全に復元される。
4. シリアライズの仕組み:典型的な特徴
XML シリアライズにはいくつかルールがある:
• シリアライズ対象のクラスはパブリックなデフォルトコンストラクタ(引数なしの public)を持っていること。
• シリアライズされるプロパティは public で、getter と setter の両方が必要。private メンバーは無視される。
• 読み取り専用のプロパティや static、abstract なメンバーは動作しない。
ルールを破ると InvalidOperationException が出て、メッセージに "XmlSerializer cannot serialize this type" のような文言が含まれる。
5. コレクションのシリアライズ:配列とリスト
複数のユーザーをシリアライズするなら、ラップ用のクラスを使う:
public class UserList
{
public List<User> Users { get; set; } = new List<User>();
}
コレクションを作ってシリアライズする:
UserList users = new UserList
{
Users = new List<User>
{
new User { FirstName = "イワン", LastName = "ペトロフ", Age = 30 },
new User { FirstName = "マリヤ", LastName = "イワノワ", Age = 25 }
}
};
XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);
生成される XML:
<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Users>
<User>
<FirstName>イワン</FirstName>
<LastName>ペトロフ</LastName>
<Age>30</Age>
</User>
<User>
<FirstName>マリヤ</FirstName>
<LastName>イワノワ</LastName>
<Age>25</Age>
</User>
</Users>
</UserList>
そのまま List<User> をシリアライズすることもできるけど、ルート要素の名前が ArrayOfUser になってしまうことがあり、連携先によっては都合が悪いことがある。
6. シリアライズを制御する属性
属性を使えば XML のフォーマットを細かく制御できる:要素名の変更、属性としてのシリアライズ、フィールドの無視など。よく使う属性は [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore] など。
属性としてシリアライズする
名前と姓を属性にして、年齢は要素にする例:
public class User
{
[XmlAttribute]
public string FirstName { get; set; }
[XmlAttribute]
public string LastName { get; set; }
[XmlElement]
public int Age { get; set; }
}
結果:
<User FirstName="イワン" LastName="ペトロフ">
<Age>30</Age>
</User>
要素名の制御
タグを読みやすい名前に変えてみる:
public class User
{
[XmlElement("名")]
public string FirstName { get; set; }
[XmlElement("姓")]
public string LastName { get; set; }
[XmlElement("年齢")]
public int Age { get; set; }
}
生成される XML:
<User>
<名>イワン</名>
<姓>ペトロフ</姓>
<年齢>30</年齢>
</User>
プロパティの無視
シリアライズしたくないプロパティは [XmlIgnore] を付ける:
public class User
{
public string FirstName { get; set; }
[XmlIgnore]
public string InternalCode { get; set; }
}
7. 階層とネストしたオブジェクトのシリアライズ
ネストしたクラスはそのまま入れ子の XML 要素になる:
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class User
{
public string FirstName { get; set; }
public Address Address { get; set; }
}
var user = new User
{
FirstName = "アンナ",
Address = new Address { City = "ネオングラード", Street = "ヴィヤゾヴァヤ" }
};
XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);
XML:
<User>
<FirstName>アンナ</FirstName>
<Address>
<City>ネオングラード</City>
<Street>ヴィヤゾヴァヤ</Street>
</Address>
</User>
8. 文字列へのシリアライズ/文字列からのデシリアライズ(ファイルを使わない)
文字列へシリアライズ
using System.Text;
var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();
serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);
文字列からのデシリアライズ
var xml = "<User><FirstName>アンナ</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);
9. 例:アプリデータのエクスポート/インポート
ユーザーリストを XML ファイルにエクスポート/インポートする例:
public static void ExportUsers(UserList users, string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Create);
serializer.Serialize(fs, users);
}
public static UserList ImportUsers(string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Open);
return (UserList)serializer.Deserialize(fs);
}
用途: バックアップ、他サービスとのデータ交換、統合。面接でも XmlSerializer に関する質問は意外とよく出る — 特に政府系や銀行系と仕事をしている会社では頻出。
10. よくあるエラーと注意点
フィールドが public でないとシリアライズされない。
読み取り専用プロパティは無視される。
インターフェース型や抽象クラス型のプロパティはシリアライズできない(serializer はサポートしない)。
型 DateTime は XSD フォーマットでシリアライズされる(詳細は ドキュメント を参照)。
配列や List<T> は予測可能に動くが、ObservableCollection<T> のようなコレクションだと外部システム側で予期せぬ挙動になることがある。
エンコーディングの注意:StreamWriter を明示的なエンコーディング指定なしで使うと出力が UTF-16 になってしまうことがある。互換性のために多くの環境では UTF-8 を期待しているので、エンコーディングは明示的に指定しよう。
GO TO FULL VERSION