web-dev-qa-db-ger.com

Konvertieren Sie JObject in Dictionary <string, object>. Ist es möglich?

Ich habe eine Web-API-Methode, die eine beliebige Json-Nutzlast in eine JObject-Eigenschaft akzeptiert. Daher weiß ich nicht, was kommt, muss aber immer noch in .NET-Typen übersetzt werden. Ich hätte gerne einen Dictionary<string,object>, damit ich damit umgehen kann, wie ich will.

Ich habe viel gesucht, aber nichts gefunden und eine unordentliche Methode für diese Konvertierung gestartet, Schlüssel für Schlüssel, Wert für Wert. Gibt es eine einfache Möglichkeit, dies zu tun?

Eingabe ->

JObject person = new JObject(
    new JProperty("Name", "John Smith"),
    new JProperty("BirthDate", new DateTime(1983, 3, 20)),
    new JProperty("Hobbies", new JArray("Play football", "Programming")),
    new JProperty("Extra", new JObject(
        new JProperty("Foo", 1),
        new JProperty("Bar", new JArray(1, 2, 3))
    )
)

Vielen Dank!

53
tucaz

Am Ende habe ich eine Mischung aus beiden Antworten verwendet, da es keiner wirklich gelungen ist.

ToObject () kann die erste Eigenschaftsebene in einem JSON-Objekt ausführen, verschachtelte Objekte werden jedoch nicht in Dictionary () konvertiert.

Es ist auch nicht nötig, alles manuell zu erledigen, da ToObject () mit den Eigenschaften der ersten Ebene ziemlich gut ist.

Hier ist der Code:

public static class JObjectExtensions
{
    public static IDictionary<string, object> ToDictionary(this JObject @object)
    {
        var result = @object.ToObject<Dictionary<string, object>>();

        var JObjectKeys = (from r in result
                           let key = r.Key
                           let value = r.Value
                           where value.GetType() == typeof(JObject)
                           select key).ToList();

        var JArrayKeys = (from r in result
                          let key = r.Key
                          let value = r.Value
                          where value.GetType() == typeof(JArray)
                          select key).ToList();

        JArrayKeys.ForEach(key => result[key] = ((JArray)result[key]).Values().Select(x => ((JValue)x).Value).ToArray());
        JObjectKeys.ForEach(key => result[key] = ToDictionary(result[key] as JObject));

        return result;
    }
}

Es kann Edge-Fälle geben, in denen es nicht funktioniert und die Leistung nicht die beste Qualität ist.

Danke Leute!

20
tucaz

Wenn Sie JObject-Objekte haben, kann Folgendes funktionieren:

JObject person;
var values = person.ToObject<Dictionary<string, object>>();

Wenn Sie keine JObject haben, können Sie eine mit der Erweiterungsmethode Newtonsoft.Json.Linq erstellen:

using Newtonsoft.Json.Linq;

var values = JObject.FromObject(person).ToObject<Dictionary<string, object>>();

Andernfalls könnte diese Antwort Sie in die richtige Richtung weisen, da eine JSON-Zeichenfolge zu einem Dictionary deserialisiert wird.

var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
97

Hier ist die Inception-Version: Ich habe den Code geändert, um recurse JArrays ein JObjects in JArrays/JObjects zu schachteln, was die akzeptierte Antwort nicht darstellt, wie von @Nawaz angegeben.

using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;

public static class JsonConversionExtensions
{
    public static IDictionary<string, object> ToDictionary(this JObject json)
    {
        var propertyValuePairs = json.ToObject<Dictionary<string, object>>();
        ProcessJObjectProperties(propertyValuePairs);
        ProcessJArrayProperties(propertyValuePairs);
        return propertyValuePairs;
    }

    private static void ProcessJObjectProperties(IDictionary<string, object> propertyValuePairs)
    {
        var objectPropertyNames = (from property in propertyValuePairs
            let propertyName = property.Key
            let value = property.Value
            where value is JObject
            select propertyName).ToList();

        objectPropertyNames.ForEach(propertyName => propertyValuePairs[propertyName] = ToDictionary((JObject) propertyValuePairs[propertyName]));
    }

    private static void ProcessJArrayProperties(IDictionary<string, object> propertyValuePairs)
    {
        var arrayPropertyNames = (from property in propertyValuePairs
            let propertyName = property.Key
            let value = property.Value
            where value is JArray
            select propertyName).ToList();

        arrayPropertyNames.ForEach(propertyName => propertyValuePairs[propertyName] = ToArray((JArray) propertyValuePairs[propertyName]));
    }

    public static object[] ToArray(this JArray array)
    {
        return array.ToObject<object[]>().Select(ProcessArrayEntry).ToArray();
    }

    private static object ProcessArrayEntry(object value)
    {
        if (value is JObject)
        {
            return ToDictionary((JObject) value);
        }
        if (value is JArray)
        {
            return ToArray((JArray) value);
        }
        return value;
    }
}
13
Uli

Klingt nach einem guten Anwendungsfall für Erweiterungsmethoden - ich hatte etwas herumliegen, das ziemlich einfach zu Json.NET konvertiert werden konnte (Danke NuGet!):

Natürlich wird dies schnell zusammen gehackt - Sie möchten es aufräumen usw.

public static class JTokenExt
{
    public static Dictionary<string, object> 
         Bagify(this JToken obj, string name = null)
    {
        name = name ?? "obj";
        if(obj is JObject)
        {
            var asBag =
                from prop in (obj as JObject).Properties()
                let propName = prop.Name
                let propValue = prop.Value is JValue 
                    ? new Dictionary<string,object>()
                        {
                            {prop.Name, prop.Value}
                        } 
                    :  prop.Value.Bagify(prop.Name)
                select new KeyValuePair<string, object>(propName, propValue);
            return asBag.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }
        if(obj is JArray)
        {
            var vals = (obj as JArray).Values();
            var alldicts = vals
                .SelectMany(val => val.Bagify(name))
                .Select(x => x.Value)
                .ToArray();
            return new Dictionary<string,object>()
            { 
                {name, (object)alldicts}
            };
        }
        if(obj is JValue)
        {
            return new Dictionary<string,object>()
            { 
                {name, (obj as JValue)}
            };
        }
        return new Dictionary<string,object>()
        { 
            {name, null}
        };
    }
}
3
JerKimball

Hier ist eine einfachere Version:

    public static object ToCollections(object o)
    {
        var jo = o as JObject;
        if (jo != null) return jo.ToObject<IDictionary<string, object>>().ToDictionary(k => k.Key, v => ToCollections(v.Value));
        var ja = o as JArray;
        if (ja != null) return ja.ToObject<List<object>>().Select(ToCollections).ToList();
        return o;
    }

Wenn Sie C # 7 verwenden, können wir Pattern Matching verwenden, wo es so aussehen würde:

    public static object ToCollections(object o)
    {
        if (o is JObject jo) return jo.ToObject<IDictionary<string, object>>().ToDictionary(k => k.Key, v => ToCollections(v.Value));
        if (o is JArray ja) return ja.ToObject<List<object>>().Select(ToCollections).ToList();
        return o;
    }
1