Подтвердить что ты не робот

Преобразование JObject в словарь <string, object>. Является ли это возможным?

У меня есть метод web api, который принимает произвольную полезную нагрузку json в свойство JObject. Поэтому я не знаю, что произойдет, но мне все равно нужно перевести его на типы .NET. Я хотел бы иметь словарь, чтобы я мог справиться с ним, в любом случае, я хочу.

Я много искал, но ничего не мог найти и закончил тем, что начал беспорядочный метод для этого преобразования, ключ от ключа, значение по значению. Есть ли простой способ сделать это?

Вход →

JObject person = new JObject(
    new JProperty("Name", "John Smith"),
    new JProperty("BirthDate", new DateTime(1983, 3, 20)),
    new JProperty("Hobbies", new JArray("Play footbal", "Programming")),
    new JProperty("Extra", new JObject(
        new JProperty("Foo", 1),
        new JProperty("Bar", new JArray(1, 2, 3))
    )
)

Спасибо!

4b9b3361

Ответ 1

В итоге я использовал сочетание обоих ответов, так как никто не прибил его.

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

У этого могут быть краевые случаи, когда он не будет работать, а производительность не является самым сильным его качеством.

Спасибо, ребята!

Ответ 2

Если у вас есть объекты JObject, может работать следующее:

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

В противном случае этот ответ может указывать на вас в правильном направлении, поскольку он десериализует строку JSON в словаре.

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

Ответ 3

Здесь начальная версия: я изменил код на recurse JArrays JObjects, вложенные в JArrays/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;
    }
}

Ответ 4

Звучит как хороший вариант использования для методов расширения - у меня было что-то лежащее, что было довольно просто преобразовать в 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}
        };
    }
}