プログラムの事とか

お約束ですが「掲載内容は私個人の見解です」

.NET で JSON を読んだ時に認知していないデータを保持したい

.NET 7 で System.Text.Json 使ったときの話です

最近のシステム間のデータのやり取りでは結構な割合でJSONを使うことがあります。ちょっと前までXMLだった気がしますがどんどんJSONになっていますね。(表記する時JSON なのか Json なのか json なのかよくわからない)

想定

{
"a":1,
"b":2
}

を受け取って、"a" をインクリメントして返したい場合、みたいな感じ

var json = @"{""a"":1,""b"":2}";

var t = JsonSerializer.Deserialize<Test>(json);
t.A++;

Console.WriteLine(JsonSerializer.Serialize(t)); // {"a":2,"b":2}

public class Test
{
    [JsonPropertyName("a")]
    public int A { get; set; }
    [JsonPropertyName("b")]
    public int B { get; set; }
}

これでいいですね?

想定外

JSONXML は拡張性があるのが売りです。ほかのシステムで上記 JSON を拡張してしまいました

{
"a":1,
"b":2,
"c":3,
"d":{
  "e":4
}}

これを先ほどのアプリに読み込ませると、当然ですがDeserialize時に c 以降が消えてしまいます

var json = @"{""a"":1,""b"":2,""c"":3,""d"":{""e"":4}}";

var t = JsonSerializer.Deserialize<Test>(json);
t.A++;

Console.WriteLine(JsonSerializer.Serialize(t)); // {"a":2,"b":2}

解決策

クラスの定義(↑ではTest)にJsonExtensionDataAttributeを付けた Dictionary を追加してあげます

var json = @"{""a"":1,""b"":2,""c"":3,""d"":{""e"":4}}";

var t = JsonSerializer.Deserialize<Test>(json);
t.A++;

Console.WriteLine(JsonSerializer.Serialize(t)); // {"a":2,"b":2,"c":3,"d":{"e":4}}

public class Test
{
    [JsonPropertyName("a")]
    public int A { get; set; }
    [JsonPropertyName("b")]
    public int B { get; set; }

    [JsonExtensionData]
    public Dictionary<string, object> AdditionalData { get; set; }
}

できました

XMLの場合

System.Xml.Serialization を使う場合は同じような感じで

[XmlAnyElement]
[XmlText]
public XmlNode[] ChildNodes { get; set; }

で行けるとおもいます

まとめ

JSONXML も 解決策は stackoverflow で見つけたんですが、日本語でもどういうキーワードで調べればいいのかがよくわからない問題だったので英語ではさらに苦労しました、という話でした(?)