結論
OSSを導入できる環境ならJSON.NETで問題なく実現できる。.NET Frameworkだけで使用可能なXmlSerializerも、扱いが少し面倒だが同等に実現できる。
YamlDotNetは、増加は問題ないが減少で問題が出る。
調べた内容
クラスの中身を文字列化してファイル保存する処理(いわゆるシリアライズ)について、主要ないくつかのライブラリで「クラスのメンバが増減しても、デシリアライズできるかどうか」を調べてみた。
確認したのは実際の動作であり、ドキュメントの記載ではない。そのため、残念ながら今後のバージョンアップなどで動作が変わる可能性はある。
なぜ調べたのか
シリアライズは、設定ファイルなどを作成する上でかなり便利に使える。設定項目ごとに処理を書く必要が無いため、項目が多数になることの多い設定ファイルに向いていると思う。
しかし、設定ファイルはバージョンアップ時に設定項目が増減することが多い。 そのため、クラスのメンバが増減した場合でもそのファイルを読み込み(デシリアライズ)できるかどうかが、採用する上では重要なポイントになる。
その点は、ドキュメントにはなかなか明記されていない。元々想定された使い方と違っているので、仕様として保証されていないのは仕方ない。
仕方ないので、バージョンを決めてその実際の動作を調べてみた。
結果のデータ
JSON.NET
.NET Framework 4.5.2 + Newtonsoft.Json 12.0.2 を使用。
変数の追加:デシリアライズ成功。追加された変数は、new時のデフォルト値となった
変数の削除:デシリアライズ成功。削除された変数は、無視された。
注意点:Listにデフォルト値がある場合、デシリアライズ時に上書きではなく追加される(直観と反する動き)
https://github.com/suusanex/sample_json_serialize
YamlDotNet
YamlDotNet 6.1.2 + .NET Framework 4.7.2を使用。
変数の追加:デシリアライズ成功。追加された変数は、new時のデフォルト値となった
変数の削除:デシリアライズ失敗。同名の変数が必ず存在している必要があるため、不要になった変数であっても、過去バージョンとの互換のために残しておく必要がある。プレフィックスobsolete_を付けた変数名を用意し、YamlMemberのAliasで過去バージョンの名前を指定することで、不要な変数を分かりやすく分類することはできた。
リストの読み込み:読み込み対象のリストにデフォルト値がある場合、上書きされる(デフォルト値は消える)。直観通りの動作。
https://github.com/suusanex/sample_yamldotnet_serialize
XmlSerializer
.NET Framework 4.7.2を使用。
前提条件:XmlSerializerは、シリアライズした時のXMLのルート要素が「クラス名」になる。そのため、別のクラスに対してデシリアライズすることができない。サンプルでは、名前空間を分けることで新旧のクラスのクラス名を同一にしている。
変数の追加:デシリアライズ成功。追加された変数は、new時のデフォルト値となった
変数の削除:デシリアライズ成功。削除された変数は、無視された。
注意点:Listにデフォルト値がある場合、デシリアライズ時に上書きではなく追加される(直観と反する動き)
https://github.com/suusanex/sample_xmlserializer_serialize
結果の検討
.NET Frameworkだけを使用するXmlSerializerでも、バージョンアップに耐える処理は実現可能だった。JSON.NETも、それと同様の動きになった。
XmlSerializerは前提条件があるし、設定ファイルくらいの目的だとXMLはパワフルすぎる感があるので、気軽に使うならJSON.NETの方が良さそうだ。(OSSを採用可能な案件であれば)
YamlDotNetは意外にも、YAML内に存在する値がクラスに定義されていないとエラーになってしまう。デシリアライザの設定などで回避できないかを調べたが、良い方法が見つからなかった。
2020/2/14追記:見つかった。この記事を参照。
YAMLにはアンカーとエイリアスがあるためC#のクラスとはXMLやJSON以上に相性が良く、最有力候補だと思ったが・・・。バージョンアップがありうる設定ファイルに使用するには、注意が必要になるようだ。