Convert JObject into Dictionary. Is it possible?

前端 未结 5 844
没有蜡笔的小新
没有蜡笔的小新 2020-12-08 03:35

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

相关标签:
5条回答
  • 2020-12-08 04:16

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

    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}
            };
        }
    }
    
    0 讨论(0)
  • 2020-12-08 04:19

    If you have JObject objects, the following might work:

    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:

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

    Otherwise, this answer might point you in the right direction, as it deserializes a JSON string to a Dictionary.

    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
    
    0 讨论(0)
  • 2020-12-08 04:24

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

    There's also no need to do everything manually as ToObject() is pretty good with first level properties.

    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!

    0 讨论(0)
  • 2020-12-08 04:24

    Here's the inception version: I've modified the code to recurse JArrays an JObjects nested in JArrays/JObjects, which the accepted answer does not, as pointed out by @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;
        }
    }
    
    0 讨论(0)
  • 2020-12-08 04:31

    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:

        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;
        }
    
    0 讨论(0)
提交回复
热议问题