【问题标题】:Convert JObject into Dictionary<string, object>. Is it possible?将 JObject 转换为 Dictionary<string, object>。可能吗?
【发布时间】:2013-01-30 23:04:48
【问题描述】:

我有一个 Web API 方法,可以将任意 json 有效负载接收到 JObject 属性中。因此,我不知道会发生什么,但我仍然需要将其转换为 .NET 类型。我想要一个Dictionary&lt;string,object&gt;,这样我就可以随心所欲地处理它。

我搜索了很多,但找不到任何东西,最后开始了一个混乱的方法来做这个转换,一个键一个键,一个值一个值。有什么简单的方法吗?

输入->

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

谢谢!

【问题讨论】:

标签: c# .net json json.net


【解决方案1】:

如果您有 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>>();

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

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

【讨论】:

  • +1 因为如果你有一个原语字典,这会特别有效。例如,这行代码非常适合我: Dictionary feeChanges = dict.feeChanges.ToObject>();
  • DeserializeObject&lt;Dictionary&lt;string, object&gt;&gt; 非常适合我;我最终通过DeserializeObject&lt;Dictionary&lt;string, object&gt;[]&gt; 将其转换为我需要的字典数组。
【解决方案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;
    }

【讨论】:

    【解决方案3】:

    这是初始版本:我已将代码修改为 recurse JArrays an JObjects nested in JArrays/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;
        }
    }
    

    【讨论】:

      【解决方案4】:

      我最终混合使用了这两个答案,因为没有一个真正能解决问题。

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

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

      谢谢大家!

      【讨论】:

      • 如果数组的值本身是 JSON(即JObject)怎么办?您没有将它们转换为 Dictionary&lt;string,object&gt;
      • @Nawaz,我认为他确实这样做了——这里的倒数第二行代码为内部JObjects 递归调用该方法。
      • @BrainSlugs83:是的。它递归调用它,但JArrays的元素仍然可以是JOBjectJArrray,然后需要将它们转换为C#数组C#字典 ,代码没有这样做。
      • @BrainSlugs83 我知道这是您发表此评论后的一段时间,但您是 100% 正确的。在您的 JArray 由 JArray 组成的场景中,您将如何填充密钥?似乎 Dictionary 数据结构在没有属性名称的情况下可能有两个嵌套级别。
      【解决方案5】:

      听起来像是扩展方法的一个很好的用例 - 我有一些东西可以很简单地转换为 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}
              };
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-27
        相关资源
        最近更新 更多