C# のデスクトップアプリで設定を保存する方法は色々と選択肢があっていつも悩む。
- ApplicationSettingsBase
- レジストリ
- INIファイル
- 自前で処理
個人的には自作の設定クラスを定義してシリアライズする方法をよく使うが、先日マルチラインが有効になっているテキストボックスの内容を保存したところ「逆XMLシリアル化時に改行が消える」という症状にぶちあたったので解決策をメモっておく。
設定クラス
とりあえず設定値を格納するクラスを定義。
public class Test { public string Value = string.Empty; }
実際はもっと大量に項目があるがサンプルなので一つだけ。
当然シリアル化できない型を含んではダメ。(Dictionaryとか)
問題が発生したシリアル化の方法
改行が消えてしまった時に使用していたクラスのソース。
using System.IO; using System.Xml; using System.Xml.Serialization; namespace Hoge { public class Config { public static void Save<T>(string filename, object o) { using (StreamWriter writer = new StreamWriter(filename)) { XmlSerializer serializer = new XmlSerializer(typeof(T)); serializer.Serialize(writer, o); } } public static T Load<T>(string filename) { using (StreamReader reader = new StreamReader(filename)) { XmlSerializer serializer = new XmlSerializer(typeof(T)); return (T)serializer.Deserialize(reader); } } } }
型引数を使って使い回しがきくようにしてあるが、StreamReader と StreamWriter と XmlSerializer を使ったシンプルな方法。
使い方
ファイル名「config.xml」に button1 で保存、button2 で復元。
private void button1_Click(object sender, EventArgs e) { Test test = new Test(); test.Value = textBox1.Text; Hoge.Config.Save<Test>("config.xml", test); } private void button2_Click(object sender, EventArgs e) { Test test = Hoge.Config.Load<Test>("config.xml"); textBox1.Text = test.Value; }
テキストボック(マルチライン)から取得した文字列をシリアル化し、逆シリアル化した文字列をテキストボックスに戻しているだけ。
この方法でシリアル化されたxmlファイルをテキストエディタで見ると改行は維持されているのだが、逆シリアル化してテキストボックスに値を戻した時に改行が削除されてしまう。
修正したシリアル化の方法
ググってみたところ、XMLで空白だけの値が削除されてしまう問題を PreserveWhitespace プロパティの設定で回避できるらしいのだが、
XmlDocument.PreserveWhitespace プロパティ
この方法で逆シリアル化すると改行コードも維持されるとのこと。
残念ながら XmlSerializer に同様のプロパティが無いので、ちょっと遠回りして回避する。
手順としては次の通り。
- XmlDocument のインスタンスを生成
- PreserveWhitespace を true 設定
- XMLファイルを読み込み
- XmlNodeReader でノードツリーの読み込み
- XmlSerializer で逆シリアル化
public class Config { public static void Save<T>(string filename, object o) { using (StreamWriter writer = new StreamWriter(filename)) { XmlSerializer serializer = new XmlSerializer(typeof(T)); serializer.Serialize(writer, o); } } public static T Load<T>(string filename) { XmlDocument doc = new XmlDocument(); doc.PreserveWhitespace = true; doc.Load(filename); using (XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement)) { XmlSerializer serializer = new XmlSerializer(typeof(T)); return (T)serializer.Deserialize(reader); } } }
まとめ
結果として改行は削除されなくなった。
簡単に検証したところ XmlSerializer で逆シリアル化された時に、改行コードが CR+LF (\r+\n) から LF (\n) に修正されるのが原因らしい。
しかも .NET Framework のテキストボックスは LF だけを改行コードとして認識しない為に改行が削除されたように見えてしまう。(実際逆シリアル化したテキストボックスをカーソルキーで右に移動させるとLFの位置で引っかかる。)
なので削除されたように見えいたのが修正されたというのが正しいのかも。