rksoftware

Visual Studio とか C# とかが好きです

C# で定義が未知の Json を扱う (.NET Core / System.Text.Json / System.Dynamic.ExpandoObject)

未知の Json を扱う一連の記事をまとめた記事を書きました。
こちらの記事で一気読みできます。

■ 現行世代の Json API / System.Text.Json

Json を扱う際には .NET Framework 時代には Json.NET というライブラリがよく使われていました。今の時代 (.NET Core 3 以降) は System.Text.Json を使用します。

未知の Json を扱う記事を以前に書きました。 しかし、少し嬉しくないことに多段の構造を持った Json を System.Dynamic.ExpandoObject にデシリアライズしてもあまり嬉しい感じではありませんでした。

■ コンバートするコード

あまり嬉しくなかったので、雑に JsonDucument で デシリアライズした後に ExpandoObject に変えるコードを書いてみました。

static System.Dynamic.ExpandoObject Parse(string json)
{
    using var document = System.Text.Json.JsonDocument.Parse(json);
    return toExpandoObject(document.RootElement);

    static object propertyValue(System.Text.Json.JsonElement elm) =>
        elm.ValueKind switch
        {
            System.Text.Json.JsonValueKind.Null => null,
            System.Text.Json.JsonValueKind.Number => elm.GetDecimal(),
            System.Text.Json.JsonValueKind.String => elm.GetString(),
            System.Text.Json.JsonValueKind.False => false,
            System.Text.Json.JsonValueKind.True => true,
            System.Text.Json.JsonValueKind.Array => elm.EnumerateArray().Select(m => propertyValue(m)).ToArray(),
            _ => toExpandoObject(elm),
        };

    static System.Dynamic.ExpandoObject toExpandoObject(System.Text.Json.JsonElement elm) =>
        elm.EnumerateObject()
        .Aggregate(
            new System.Dynamic.ExpandoObject(),
            (exo, prop) => { ((IDictionary<string, object>)exo).Add(prop.Name, propertyValue(prop.Value)); return exo; });
}

理屈は、まあ普通の C# コードなので読んでもらえば分かると思います。

使い方

メソッドなので普通に使えます。

dynamic expandoObject = Parse(
    @"{ ""prop1"": ""value1"", ""prop2"": 2, ""prop3"": { ""p31"": 31 , ""p32"": [1, ""2"", { ""p321"": ""value321"", ""p322"": ""value322"" } ] }}");

試しに、プロパティを追加したり削除したりなどして、シリアライズして結果を見てみましょう。

dynamic expandoObject = Parse(
    @"{ ""prop1"": ""value1"", ""prop2"": 2, ""prop3"": { ""p31"": 31 , ""p32"": [1, ""2"", { ""p321"": ""value321"", ""p322"": ""value322"" } ] }}");

// プロパティを追加してみる
expandoObject.prop3.p33 = 33;

// プロパティを削除してみる
((IDictionary<string, object>)expandoObject.prop3).Remove("p31");

// シリアライズしてみる
Console.WriteLine(
    System.Text.Json.JsonSerializer.Serialize(expandoObject)
);
// {"prop1":"value1","prop2":2,"prop3":{"p32":[1,"2",{"p321":"value321","p322":"value322"}],"p33":33}} と出力される

出力結果

{"prop1":"value1","prop2":2,"prop3":{"p32":[1,"2",{"p321":"value321","p322":"value322"}],"p33":33}}

それっぽく動いているようです。