将JObject转换为Dictionary <string,object>。可能吗?


77

我有一个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))
    )
)

谢谢!


2
两件事,JObject已经实现了Dictionary <string,JToken>。问题是,您打算如何处理子属性。您的Dictionary中的值是否为另一个Dictionary <string,?>?
Rich

是的@Rich,子属性将是另一个Dictionary <string,object>
tucaz

另请参见如何使用JSON.NET反序列化为嵌套/递归字典和列表?。第ToObject(JToken)一个答案中的helper方法将使用最少的代码进行此转换。
Brian Rogers

Answers:


139

如果您有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字符串反序列化为Dictionary。

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

+1,因为如果您有原始字典,这会特别好用。例如,以下代码行对我而言非常理想:Dictionary <string,decimal> feeChanges = dict.feeChanges.ToObject <Dictionary <string,decimal >>();
allen1 2015年

这项DeserializeObject<Dictionary<string, object>>工作对我来说很棒;我最终通过将其转换为满足我需要的字典数组DeserializeObject<Dictionary<string, object>[]>
BrainSlugs83 2013年

25

我最终使用了两个答案的混合,因为没有一个真正确定。

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

它可能在某些极端情况下无法正常工作,并且性能不是其最强的质量。

谢谢你们!


如果数组的值JObject本身就是JSON怎么办?您不会将它们转换为Dictionary<string,object>
Nawaz,

@Nawaz,我想他是的-在这里,倒数第二行代码为inner JObjects递归调用该方法。
BrainSlugs83 2013年

@ BrainSlugs83:是的。它递归地调用它,但仍然JArrays可以将的元素JOBjectJArrray,然后将其转换为C#arrayC#Dictionary,而代码则不执行。
Nawaz

@ BrainSlugs83我知道您发布此评论已经有一段时间了,但是您100%是正确的。在您的JArray由JArray组成的情况下,如何填充密钥?似乎Dictionary <string,object>数据结构在可能没有属性名称的两个嵌套级别中处于平坦状态。
Miek

16

这是初始版本:我已经修改了代码,以递归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;
    }
}

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

2

这是一个简单的版本:

    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;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.