101 votes

Convertit JObject en Dictionary<string, object>. Est-ce possible ?

J'ai une méthode d'API web qui accepte une charge utile json arbitraire dans un fichier de type JObject propriété. En tant que tel, je ne sais pas ce qui va se passer, mais je dois quand même le traduire en types .NET. J'aimerais avoir un Dictionary<string,object> pour que je puisse m'en occuper comme je le veux.

J'ai beaucoup cherché, mais je n'ai rien trouvé et j'ai fini par créer une méthode désordonnée pour effectuer cette conversion, clé par clé, valeur par valeur. Existe-t-il un moyen simple de le faire ?

Entrée ->

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

Merci !

183voto

Virtlink Points 12475

Si vous avez JObject la méthode suivante pourrait fonctionner :

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

Si vous ne disposez pas d'un JObject vous pouvez en créer un avec la Newtonsoft.Json.Linq méthode d'extension :

using Newtonsoft.Json.Linq;

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

Autrement, cette réponse pourrait vous mettre sur la bonne voie, car il désérialise une chaîne JSON vers un dictionnaire.

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

30voto

tucaz Points 2488

J'ai fini par utiliser un mélange des deux réponses car aucune ne correspondait vraiment à la réalité.

ToObject() peut traiter le premier niveau de propriétés d'un objet JSON, mais les objets imbriqués ne seront pas convertis en Dictionary().

Il n'est pas non plus nécessaire de tout faire manuellement, car ToObject() est assez performant avec les propriétés de premier niveau.

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

Il peut y avoir des cas limites où il ne fonctionne pas et les performances ne sont pas sa plus grande qualité.

Merci les gars !

21voto

Uli Points 482

Voici la version d'inception : J'ai modifié le code pour Récupération de tableaux et d'objets communs (JObjects) imbriqué dans JArrays/JObjects ce que la réponse acceptée ne fait pas, comme l'a souligné @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;
    }
}

5voto

Voici une version plus simple :

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

Si nous utilisons C# 7, nous pouvons utiliser le filtrage par motif, ce qui donne le résultat suivant :

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

3voto

JerKimball Points 8994

Cela semble être un bon cas d'utilisation pour les méthodes d'extension - j'avais quelque chose qui traînait et qui était assez simple à convertir en Json.NET (merci NuGet !):

Bien sûr, il s'agit d'un montage rapide - il faut le nettoyer, 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}
        };
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X