⚙️
XmlSerializer と DefaultValueAttribute は一緒に使うとよくないことが起きるらしい
いまさらなのですが DefaultValueAttribute
属性が付けられたフィールドを XmlSerializer
でシリアライズすると、XML に値が書き込まれません。しかも、デシリアライズするときは何もしてくれないので、データが消えてしまいます。再現するコードを書いてみます。
public class Program
{
// DefaultValue を付けたメンバーを持つクラス
public class Hoge
{
[DefaultValue(100)]
public int Piyo { get; set; }
}
private static void Main()
{
var hoge = new Hoge();
var serializer = new XmlSerializer(typeof(Hoge));
// DefaultValue と同じ値を仕込んでみる
hoge.Piyo = 100;
using (var stream = File.Open("hoge.xml", FileMode.Create, FileAccess.ReadWrite))
{
// シリアライズする
serializer.Serialize(stream, hoge);
// 最初に戻って
stream.Position = 0;
// デシリアライズする
hoge = (Hoge)serializer.Deserialize(stream);
}
// 結果を出力する
Console.WriteLine("piyo: " + hoge.Piyo);
Console.ReadLine();
}
}
これを実行すると結果はこうなります。
piyo: 0
出力される XML はこんな感じ。見やすさのために改行を入れています。確かに値が出力されていません。
<?xml version="1.0"?>
<Hoge xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
どうもこの動作は仕様らしいとのこと。
とはいえ、それでは思った動作にならないので、シリアライズする対象のクラスのコンストラクタに DefaultValueAttribute
の値で初期化するようにします。
public Hoge()
{
Array.ForEach(this.GetType().GetProperties(), x =>
{
var attr = Attribute.GetCustomAttribute(x, typeof(DefaultValueAttribute));
if (attr != null)
{
x.SetValue(this, ((DefaultValueAttribute)attr).Value, null);
}
});
}
これで実行すると、結果は以下のようになります。
piyo: 100
念のため注意点を以下に示します。
- リフレクションを使うのでパフォーマンスに影響を及ぼす可能性があります。
- インスタンスの初期化時には常に
DefaultValueAttribute
の値で初期化されます。
Discussion