C# 将 JObject 转换为 Dictionary<string, object>。是否可以?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14886800/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert JObject into Dictionary<string, object>. Is it possible?
提问by tucaz
I have a web API method that accepts an arbitrary json payload into a JObject
property. As such I don't know what's coming but I still need to translate it to .NET types. I would like to have a Dictionary<string,object>
so that I can deal with it any way I want to.
我有一个 Web API 方法,它接受一个任意的 json 负载到一个JObject
属性中。因此,我不知道会发生什么,但我仍然需要将其转换为 .NET 类型。我想要一个Dictionary<string,object>
这样我就可以以任何我想要的方式处理它。
I have searched a lot, but couldn't find anything and ended up starting a messy method to do this conversion, key by key, value by value. Is there an easy way to do it?
我已经搜索了很多,但找不到任何东西,并最终开始了一个凌乱的方法来进行这个转换,一个键一个键,一个值一个值。有没有简单的方法来做到这一点?
Input ->
输入 ->
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))
)
)
Thanks!
谢谢!
采纳答案by tucaz
I ended up using a mix of both answers as none really nailed it.
我最终使用了两种答案的混合,因为没有人真正做到这一点。
ToObject() can do the first level of properties in a JSON object, but nested objects won't be converted to Dictionary().
ToObject() 可以在 JSON 对象中执行第一级属性,但嵌套对象不会转换为 Dictionary()。
There's also no need to do everything manually as ToObject() is pretty good with first level properties.
也无需手动完成所有操作,因为 ToObject() 具有第一级属性非常好。
Here is the 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;
}
}
It might have edge cases where it won't work and the performance is not the strongest quality of it.
它可能存在无法正常工作的边缘情况,并且性能不是它的最强品质。
Thanks guys!
谢谢你们!
回答by Daniel A.A. Pelsmaeker
If you have JObject
objects, the following might work:
如果您有JObject
对象,则以下方法可能有效:
JObject person;
var values = person.ToObject<Dictionary<string, object>>();
If you do not have a JObject
you can create one with the Newtonsoft.Json.Linq
extension method:
如果您没有,则JObject
可以使用Newtonsoft.Json.Linq
扩展方法创建一个:
using Newtonsoft.Json.Linq;
var values = JObject.FromObject(person).ToObject<Dictionary<string, object>>();
Otherwise, this answermight point you in the right direction, as it deserializes a JSON string to a Dictionary.
否则,此答案可能会为您指明正确的方向,因为它将 JSON 字符串反序列化为字典。
var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
回答by JerKimball
Sounds like a good use case for extension methods - I had something lying around that was pretty straightforward to convert to Json.NET (Thanks NuGet!):
听起来像是扩展方法的一个很好的用例 - 我有一些东西可以非常简单地转换为 Json.NET(感谢 NuGet!):
Of course, this is quickly hacked together - you'd want to clean it up, etc.
当然,这很快就被破解了——你想清理它,等等。
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}
};
}
}
回答by Uli
Here's the inception version: I've modified the code to recurse JArrays an JObjects nested inJArrays/JObjects, which the accepted answer does not, as pointed out by @Nawaz.
这是初始版本:正如@Nawaz所指出的,我修改了代码以递归 JArrays 和嵌套在JArrays/JObjects 中的 JObjects,而接受的答案没有。
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;
}
}
回答by Esben Skov Pedersen
Here is a simpler 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;
}
If using C# 7 we can use pattern matching where it would look like this:
如果使用 C# 7,我们可以使用模式匹配,它看起来像这样:
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;
}