プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2020/06/18

[C#] リストをデシリアライズしたときに項目が追加登録されてしまう

event_note2020/06/17 23:16

リストの初期値が設定されているクラスのデータを Newtonsoft.Json (Json.NET) でデシリアライズすると、そのリストの項目は初期値を含んだうえでデシリアライズされた値が追加登録されていました。
シリアライズされた内容とデシリアライズされた内容が異なるので、いや、ほんとはまりました。

環境

  • Visual Studio 2017
  • .NET Core 2.2

サンプルコード

以下のようなクラスをシリアライズ、デシリアライズするとします。

public class SampleData
{
    public IList<int> Hoge { get; set; } = new List<int> { 1 };
}
var data = new SampleData();

// シリアライズ
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);

// デシリアライズ(このタイミングでリストの項目が2つになっている!)
var deserialized = JsonConvert.DeserializeObject<SampleData>(serialized);
Console.WriteLine($"Hoge Count: {deserialized.Hoge.Count()}");

// もう一度シリアライズして確認
var serialized2 = JsonConvert.SerializeObject(deserialized);
Console.WriteLine(serialized2);

出力結果

{"Hoge":[1]}
Hoge Count: 2
{"Hoge":[1,1]}

デシリアライズ時に項目が1つ増えています。
シリアライズした内容はリストを置換するではなく、リストに追加登録されるからです。
いや、これほんとにはまった。

ObjectCreationHandling.Replace を指定する

初期値を含めないようにするにはデシリアライズ時のオプションで ObjectCreationHandling.Replace を指定する必要があります。

var deserialized = JsonConvert.DeserializeObject<SampleData>(serialized, new JsonSerializerSettings
{   
    ObjectCreationHandling = ObjectCreationHandling.Replace,
});

こちらの動作のほうが直観的だと思うので、こちらが既定値のほうが良いと思うのですが、私だけでしょうか?
ちなみに DataContractJsonSerializer を使った場合は既定で置換してくれました。