web-dev-qa-db-ja.com

Json.NET:ネストされた辞書の逆シリアル化

オブジェクトをDictionaryJsonConvert.DeserializeObject<IDictionary<string,object>>(json))に逆シリアル化すると、ネストされたオブジェクトはJObjectsに逆シリアル化されます。ネストされたオブジェクトをDictionarysに強制的に逆シリアル化することは可能ですか?

34
Daniel

CustomCreationConverter実装を提供することにより、すべてのネストされたオブジェクトをDictionary<string,object>に変換する方法を見つけました。

class MyConverter : CustomCreationConverter<IDictionary<string, object>>
{
    public override IDictionary<string, object> Create(Type objectType)
    {
        return new Dictionary<string, object>();
    }

    public override bool CanConvert(Type objectType)
    {
        // in addition to handling IDictionary<string, object>
        // we want to handle the deserialization of dict value
        // which is of type object
        return objectType == typeof(object) || base.CanConvert(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject
            || reader.TokenType == JsonToken.Null)
            return base.ReadJson(reader, objectType, existingValue, serializer);

        // if the next token is not an object
        // then fall back on standard deserializer (strings, numbers etc.)
        return serializer.Deserialize(reader);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var json = File.ReadAllText(@"c:\test.json");
        var obj = JsonConvert.DeserializeObject<IDictionary<string, object>>(
            json, new JsonConverter[] {new MyConverter()});
    }
}

ドキュメント:Json.NETを使用したCustomCreationConverter

45
AlexD

代替/更新:

Stringsの辞書の辞書を逆シリアル化する必要があり、現在のJson.NET(5.0)では、CustomConverterを作成する必要はなく、(VB.Netで)使用しました:

JsonConvert.DeserializeObject(Of IDictionary(Of String, IDictionary(Of String, String)))(jsonString)

または、C#の場合:

JsonConvert.DeserializeObject<IDictionary<String, IDictionary<String, String>>(jsonString);
3
madth3

このQに遭遇したとき、私は非常に似ていますが少し複雑なニーズがありました。最初は、受け入れられた回答を適応できるかもしれないと思ったのですが、それは少し複雑に思え、結局別のアプローチをとることになりました。レガシーC++ APIの上に最新のJSONレイヤーを配置しようとしました。詳細は割愛します。要件は次のように要約されます。

  • JSONオブジェクトはDictionary<string,object>になります。

  • JSON配列はList<object>になります。

  • JSON値は、対応するプリミティブCLR値になります。

  • オブジェクトと配列は無限にネストできます。

最初にリクエスト文字列をNewtonsoft JSONオブジェクトに逆シリアル化してから、上記の要件に従ってメソッドを呼び出して変換します。

var jsonObject = JsonConvert.DeserializeObject(requestString);
var apiRequest = ToApiRequest(jsonObject);
// call the legacy C++ API ...

APIが期待する構造に変換する私のメソッドは次のとおりです。

    private static object ToApiRequest(object requestObject)
    {
        switch (requestObject)
        {
            case JObject jObject: // objects become Dictionary<string,object>
                return ((IEnumerable<KeyValuePair<string, JToken>>) jObject).ToDictionary(j => j.Key, j => ToApiRequest(j.Value));
            case JArray jArray: // arrays become List<object>
                return jArray.Select(ToApiRequest).ToList();
            case JValue jValue: // values just become the value
                return jValue.Value;
            default: // don't know what to do here
                throw new Exception($"Unsupported type: {requestObject.GetType()}");
        }
    }

私は誰かがこのアプローチが役に立つと思うことを望みます。

0
James R.