98

我有一个 Web API 方法,可以将任意 json 有效负载接受到JObject属性中。因此,我不知道会发生什么,但我仍然需要将其转换为 .NET 类型。我想要一个Dictionary<string,object>,这样我就可以以任何我想要的方式处理它。

我进行了很多搜索,但找不到任何东西,最终开始了一种混乱的方法来进行这种转换,一个键一个键,一个值一个值。有没有简单的方法来做到这一点?

输入->

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))
    )
)

谢谢!

4

5 回答 5

172

如果您有JObject对象,以下可能会起作用:

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

如果您没有,您可以使用扩展方法JObject创建一个:Newtonsoft.Json.Linq

using Newtonsoft.Json.Linq;

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

否则,此答案可能会为您指明正确的方向,因为它将 JSON 字符串反序列化为字典。

var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
于 2013-02-15T01:12:02.730 回答
30

我最终混合使用了这两个答案,因为没有一个真正能做到这一点。

ToObject() 可以做 JSON 对象的第一级属性,但嵌套对象不会转换为 Dictionary()。

也无需手动执行所有操作,因为 ToObject() 对于一级属性非常好。

这是代码:

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

它可能存在无法工作的边缘情况,并且性能不是它的最强质量。

多谢你们!

于 2013-02-15T21:22:09.413 回答
21

这是初始版本:我已经修改了代码以递归 JArrays嵌套在JArrays/JObjects中的 JObjects ,正如@Nawaz 指出的那样,接受的答案没有。

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;
    }
}
于 2016-09-22T20:03:30.047 回答
5

这是一个更简单的版本:

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

如果使用 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;
    }
于 2018-07-19T06:18:16.673 回答
3

听起来像是扩展方法的一个很好的用例——我有一些东西很容易转换为 Json.NET(感谢 NuGet!):

当然,这很快就被破解了——你想清理它,等等。

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}
        };
    }
}
于 2013-02-15T05:53:40.027 回答