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!
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!
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);
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;
}
}
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}
};
}
}
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;
}