Comment désérialiser JSON en un simple dictionnaire <chaîne, chaîne> dans ASP.NET?


682

J'ai une simple liste clé / valeur en JSON renvoyée à ASP.NET via POST. Exemple:

{ "key1": "value1", "key2": "value2"}

JE N'ESSAYE PAS DE DESERIALISER EN OBJETS .NET FORTEMENT TYPES

J'ai simplement besoin d'un vieux dictionnaire ordinaire (Of String, String) , ou d'un équivalent (table de hachage, Dictionary (Of String, Object), old-school StringDictionary - diable, un tableau 2-D de chaînes fonctionnerait pour moi.

Je peux utiliser tout ce qui est disponible dans ASP.NET 3.5, ainsi que le populaire Json.NET (que j'utilise déjà pour la sérialisation vers le client).

Apparemment, aucune de ces bibliothèques JSON ne dispose de cette capacité évidente de claquement de front - elles sont totalement axées sur la désérialisation basée sur la réflexion via des contrats forts.

Des idées?

Limites:

  1. Je ne veux pas implémenter mon propre analyseur JSON
  2. Impossible d'utiliser ASP.NET 4.0 pour le moment
  3. Je préférerais rester à l'écart de l'ancienne classe ASP.NET obsolète pour JSON

1
re: limitation 3, JavaScriptSerizlizerest utilisé dans ASP.NET MVC et n'est plus obsolète.
bdukes

17
c'est incroyable à quel point il était difficile de trouver un moyen simple de convertir une chaîne json en quelque chose que je pourrais facilement utiliser sans feuilleter de nombreux stackoverflows différents. C'est si facile dans d'autres langages, mais Java et C # semblent faire tout leur possible pour rendre la vie difficile.
user299709

Réponses:


894

Json.NET fait cela ...

string json = @"{""key1"":""value1"",""key2"":""value2""}";

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

Plus d'exemples: sérialisation de collections avec Json.NET


9
Cela fonctionne-t-il également lorsque vos valeurs sont des nombres entiers. Sont-ils automatiquement castés en «chaînes»?
Highmastdon

58
@Highmastdon Non, ce n'est pas le cas. J'ai trouvé que la meilleure façon de désérialiser dans un dictionnaire est d'utiliser dynamiccomme type pour les valeurs:JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
Erik Schierboom

1
J'ai essayé plusieurs réponses sur cette page avec une paire clé / valeur très désordonnée, et JSON.NET était la seule que j'ai essayé qui fonctionnait.
bnieland

15
Ne fonctionne pas si vous utilisez un tableau de paires de valeurs clés dans json, [{key: "a", value: "1"}, {key: "b", value:"2"}]vous devez faire quelque chose comme ceci:var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
Adrian

8
Ne fonctionne pas non plus si les valeurs sont des objets imbriqués, car json.net les crée en tant que JObjects
Kugel

100

J'ai découvert que .NET a une manière intégrée de convertir la chaîne JSON en Dictionary<String, Object>via le System.Web.Script.Serialization.JavaScriptSerializertype dans l' System.Web.Extensionsassembly 3.5 . Utilisez la méthode DeserializeObject(String).

Je suis tombé sur cela en faisant un post ajax (via jquery) de type de contenu 'application / json' sur une méthode de page .net statique et j'ai vu que la méthode (qui avait un seul paramètre de type Object) recevait par magie ce dictionnaire.


5
mais le javascriptserializer intégré est plus bogué que json.net, cette solution est meilleure. Par exemple, javascriptseralizer retournera des valeurs nulles au lieu de chaînes vides, et ne fonctionnera pas du tout pour les propriétés nullables, etc.
pilavdzice

1
@pilavdzice Sans parler du plaisir que vous avez lorsque vous essayez d'analyser des dates car il suppose le format de date non standard de MS.
Basic

16
Exemple de code rapide: var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();suivi deDictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Nate Cook

6
L'avantage de l'exemple de Nate Cook dans un cas simple est d'éviter le besoin de DLL externes. J'accède à une API à partir d'une console autonome qui ne peut s'appuyer que sur le framework .Net.
Nick.T

@pilavdzice Pouvez-vous entrer dans les détails à ce sujet? Je ne peux pas reproduire la chose "retourner null au lieu de chaînes vides", cela m'a donné une valeur de chaîne vide pourSomeData: ""
jrh

51

Pour ceux qui recherchent sur Internet et tombent sur ce post, j'ai écrit un blog sur la façon d'utiliser la classe JavaScriptSerializer.

En savoir plus ... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

Voici un exemple:

var json = "{\"id\":\"13\", \"value\": true}";
var jss = new JavaScriptSerializer();
var table = jss.Deserialize<dynamic>(json);
Console.WriteLine(table["id"]);
Console.WriteLine(table["value"]);

hm, j'ai essayé votre solution ... J'ai json comme ceci {"id": "13", "value": true} et pour moi seule la solution Dictionary <dynamic> fonctionne
Marko

ok je l'ai trouvé où est le problème ... vous devez ajouter [] après la déclaration du dictionnaire afin de désérialiser correctement ... J'ajoute aussi un commentaire à votre article de blog ... cheers;)
Marko

J'ai mis à jour ma réponse pour refléter votre ensemble de données spécifique. Cela fonctionne bien avec dynamique.
JP Richardson

Je viens d'écrire un autre analyseur JSON qui est un peu plus flexible et prend en charge Silverlight: procbits.com/2011/08/11/…
JP Richardson

41

J'ai essayé de ne pas utiliser d'implémentation JSON externe, j'ai donc désérialisé comme ceci:

string json = "{\"id\":\"13\", \"value\": true}";

var serializer = new JavaScriptSerializer(); //using System.Web.Script.Serialization;

Dictionary<string, string> values = serializer.Deserialize<Dictionary<string, string>>(json);

6
Ajouter la référence System.Web.Extensions pour utiliser System.Web.Script
Patrick Cullen

1
Je préfère cette réponse car elle est simple et utilise le .NET System.Web.Script.Serialization. Ça marche juste. J'ai même pu utiliser du JSON "invalide" comme string json = "{'id':13, 'value': true}";.
styfle

Par curiosité, existe-t-il la même méthode de désérialisation dans le dictionnaire OrdinalIgnoreCase?
batbaatar

38

J'ai eu le même problème, alors j'ai écrit cela moi-même. Cette solution se différencie des autres réponses car elle peut désérialiser à plusieurs niveaux.

Envoyez simplement la chaîne JSON à la fonction deserializeToDictionary , elle retournera un objet non fortement typé Dictionary<string, object>.

Ancien code

private Dictionary<string, object> deserializeToDictionary(string jo)
{
    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
    var values2 = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> d in values)
    {
        // if (d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
        if (d.Value is JObject)
        {
            values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
        }
        else
        {
            values2.Add(d.Key, d.Value);
        }
    }
    return values2;
}

Ex: Cela retournera l' Dictionary<string, object>objet d'une réponse Facebook JSON.

Tester

private void button1_Click(object sender, EventArgs e)
{
    string responsestring = "{\"id\":\"721055828\",\"name\":\"Dasun Sameera Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\",  hometown: {id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",}}";
    Dictionary<string, object> values = deserializeToDictionary(responsestring);
}

Remarque: la ville natale se désérilise davantage en un Dictionary<string, object> objet.

Mise à jour

Mon ancienne réponse fonctionne très bien s'il n'y a pas de tableau sur la chaîne JSON. Celui-ci se désérialise en un List<object>si un élément est un tableau.

Envoyez simplement une chaîne JSON dans la fonction deserializeToDictionaryOrList , elle renverra un objet non fortement typé Dictionary<string, object>ou List<object>.

private static object deserializeToDictionaryOrList(string jo,bool isArray=false)
{
    if (!isArray)
    {
        isArray = jo.Substring(0, 1) == "[";
    }
    if (!isArray)
    {
        var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
        var values2 = new Dictionary<string, object>();
        foreach (KeyValuePair<string, object> d in values)
        {
            if (d.Value is JObject)
            {
                values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
            }
            else if (d.Value is JArray)
            {
                values2.Add(d.Key, deserializeToDictionary(d.Value.ToString(), true));
            }
            else
            {
                values2.Add(d.Key, d.Value);
            }
        }
        return values2;
    }else
    {
        var values = JsonConvert.DeserializeObject<List<object>>(jo);
        var values2 = new List<object>();
        foreach (var d in values)
        {
            if (d is JObject)
            {
                values2.Add(deserializeToDictionary(d.ToString()));
            }
            else if (d is JArray)
            {
                values2.Add(deserializeToDictionary(d.ToString(), true));
            }
            else
            {
                values2.Add(d);
            }
        }
        return values2;
    }
}

@Jordan merci d'avoir souligné, j'ai apporté quelques modifications à ce code mais je ne l'ai pas maintenant. Ce code ne gère pas les objets JArray, je mettrai à jour le code une fois que je l'aurai.
Dasun

1
Pas de problème. Je ne mentionne que parce que l' apprentissage de la iset les asopérateurs m'a beaucoup aidé et simplifié mon propre code.
Jordan

Cela fonctionne, mais n'est pas efficace, car il appelle ToString, puis désérialise à nouveau. Regardez la réponse de Falko ci-dessous. Il ne désérialise la chaîne source qu'une seule fois.
Sergei Zinovyev

1
La réponse de Falko ne fonctionne que si vous connaissez à l'avance la structure des données. Cette solution peut être utilisée pour n'importe quelle chaîne JSON.
Dasun

16

Si vous recherchez une approche légère et sans références ajoutées, peut-être que ce morceau de code que je viens d'écrire fonctionnera (je ne peux cependant pas garantir à 100% la robustesse).

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

public Dictionary<string, object> ParseJSON(string json)
{
    int end;
    return ParseJSON(json, 0, out end);
}
private Dictionary<string, object> ParseJSON(string json, int start, out int end)
{
    Dictionary<string, object> dict = new Dictionary<string, object>();
    bool escbegin = false;
    bool escend = false;
    bool inquotes = false;
    string key = null;
    int cend;
    StringBuilder sb = new StringBuilder();
    Dictionary<string, object> child = null;
    List<object> arraylist = null;
    Regex regex = new Regex(@"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
    int autoKey = 0;
    for (int i = start; i < json.Length; i++)
    {
        char c = json[i];
        if (c == '\\') escbegin = !escbegin;
        if (!escbegin)
        {
            if (c == '"')
            {
                inquotes = !inquotes;
                if (!inquotes && arraylist != null)
                {
                    arraylist.Add(DecodeString(regex, sb.ToString()));
                    sb.Length = 0;
                }
                continue;
            }
            if (!inquotes)
            {
                switch (c)
                {
                    case '{':
                        if (i != start)
                        {
                            child = ParseJSON(json, i, out cend);
                            if (arraylist != null) arraylist.Add(child);
                            else
                            {
                                dict.Add(key, child);
                                key = null;
                            }
                            i = cend;
                        }
                        continue;
                    case '}':
                        end = i;
                        if (key != null)
                        {
                            if (arraylist != null) dict.Add(key, arraylist);
                            else dict.Add(key, DecodeString(regex, sb.ToString()));
                        }
                        return dict;
                    case '[':
                        arraylist = new List<object>();
                        continue;
                    case ']':
                        if (key == null)
                        {
                            key = "array" + autoKey.ToString();
                            autoKey++;
                        }
                        if (arraylist != null && sb.Length > 0)
                        {
                            arraylist.Add(sb.ToString());
                            sb.Length = 0;
                        }
                        dict.Add(key, arraylist);
                        arraylist = null;
                        key = null;
                        continue;
                    case ',':
                        if (arraylist == null && key != null)
                        {
                            dict.Add(key, DecodeString(regex, sb.ToString()));
                            key = null;
                            sb.Length = 0;
                        }
                        if (arraylist != null && sb.Length > 0)
                        {
                            arraylist.Add(sb.ToString());
                            sb.Length = 0;
                        }
                       continue;
                    case ':':
                        key = DecodeString(regex, sb.ToString());
                        sb.Length = 0;
                        continue;
                }
            }
        }
        sb.Append(c);
        if (escend) escbegin = false;
        if (escbegin) escend = true;
        else escend = false;
    }
    end = json.Length - 1;
    return dict; //theoretically shouldn't ever get here
}
private string DecodeString(Regex regex, string str)
{
    return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));
}

[Je me rends compte que cela viole la limitation OP # 1, mais techniquement, vous ne l'avez pas écrit, je l'ai fait]


3
C'est la seule réponse qui fonctionne pour Silverlight et sans dépendance! Silverlight n'a pas JavascriptSerializer ou Serializable. Et aucune dépendance ne signifie aucun Json.NET, RestSharp ou MiniJSON. Seul @DanCsharpster a essayé une autre solution possible, mais malheureusement, cela ne fonctionnait pas pour moi comme celui-ci.
Cœur

1
Quel est le problème avec l'ajout d'une référence à quelque chose de simple comme JSON.NET? Doit-il être aussi léger que vous ne puissiez rien référencer? Je ne dis pas que votre code ne fonctionnera pas, mais chaque fois que vous lancez le vôtre, vous courez évidemment le risque que votre code ne soit pas aussi robuste, pour des choses comme les cas limites, ou rapide comme une bibliothèque testée comme JSON.NET.
Dan Csharpster

1
Rouler le vôtre est une mauvaise idée lorsque vous avez une bonne alternative. Je ne connais aucune situation qui doive être aussi légère. Et je préfère avoir un code moins optimal, facile à lire et à modifier.
Jordan

3
J'ai écrit à l'origine ce morceau de code parce que je n'avais pas d'alternative. Considérez des choses comme Silverlight ou des fournisseurs de divers types de produits Office, où l'ajout de références externes au projet est extrêmement problématique ou impossible.
dexy

Je sais que c'est quelques années plus tard, mais c'est toujours une question très valable. Pour tous ceux qui se demandent pourquoi voudrions-nous aller si léger, eh bien, si vous travaillez avec SQL CLR C #, il n'y a que tant de bibliothèques "sûres" que vous pouvez utiliser et System.RunTime.Serializationn'en fait pas partie, malheureusement JSON.NET dépend de et donc il ne peut pas être utilisé non plus. Merci dexy pour votre excellent travail, j'ai osé l'améliorer un peu pour pouvoir désérialiser des tableaux de tableaux, voir le code mis à jour dans ma réponse ici .
Alberto Rechy

15

J'avais juste besoin d'analyser un dictionnaire imbriqué , comme

{
    "x": {
        "a": 1,
        "b": 2,
        "c": 3
    }
}

JsonConvert.DeserializeObjectn'aide pas. J'ai trouvé l'approche suivante:

var dict = JObject.Parse(json).SelectToken("x").ToObject<Dictionary<string, int>>();

Le SelectTokenvous permet de creuser jusqu'au champ souhaité. Vous pouvez même spécifier un chemin d'accès comme "x.y.z"descendre plus bas dans l'objet JSON.


JObject.Parse (json) .ToObject <Dictionary <Guid, List <int> >> () a fonctionné pour moi dans mon scénario merci
geedubb

11

System.Text.Json

Cela peut maintenant être fait en utilisant System.Text.Jsonqui est intégré à .net core 3.0. Il est désormais possible de désérialiser JSON sans utiliser de bibliothèques tierces.

var json = @"{""key1"":""value1"",""key2"":""value2""}";
var values = JsonSerializer.Deserialize<Dictionary<string, string>>(json);

Également disponible dans le package nu-get System.Text.Json si vous utilisez .Net Standard ou .Net Framework.


1
Oui! System.Text.Jsonest la voie à suivre ces jours-ci.
mfluehr

2
Oui, ça a l'air prometteur! Cependant, notez que la version par défaut de .NET Core 3.1 de System.Text.Json ne prend pas en charge la désérialisation des dictionnaires avec des clés non chaîne. Alors que mon OP portait sur des cordes, dans la pratique maintenant, j'ai beaucoup de touches Guid, donc cela m'a "mordu" lorsque j'essaie de faire le changement. Il n'a pas non plus d'équivalents de certains des attributs (requis, etc.).
richardtallent

6

Mark Rendle a posté cela comme un commentaire , je voulais le poster comme une réponse car c'est la seule solution qui a fonctionné jusqu'à présent pour renvoyer le succès et les codes d'erreur json résultant de la réponse de Google reCaptcha.

string jsonReponseString= wClient.DownloadString(requestUrl);    
IDictionary<string, object> dict = new JavaScriptSerializer().DeserializeObject(jsonReponseString) as IDictionary<string, object>;

Merci encore, Mark!


1
JavaScriptSerializer est presque obsolète. La documentation indique que nous devrions utiliser JSON.NET ( docs.microsoft.com/en-us/dotnet/api/… )
Mario Lopez

Convient également aux applications de formulaires Web héritées où vous ne souhaitez pas inclure de dépendances supplémentaires.
Andrew Grothe

5

Edit: Cela fonctionne, mais la réponse acceptée en utilisant Json.NET est beaucoup plus simple. Laisser celui-ci au cas où quelqu'un aurait besoin d'un code BCL uniquement.

Il n'est pas pris en charge par le framework .NET prêt à l'emploi. Un oubli flagrant - tout le monde n'a pas besoin de se désérialiser en objets avec des propriétés nommées. J'ai donc fini par rouler le mien:

<Serializable()> Public Class StringStringDictionary
    Implements ISerializable
    Public dict As System.Collections.Generic.Dictionary(Of String, String)
    Public Sub New()
        dict = New System.Collections.Generic.Dictionary(Of String, String)
    End Sub
    Protected Sub New(info As SerializationInfo, _
          context As StreamingContext)
        dict = New System.Collections.Generic.Dictionary(Of String, String)
        For Each entry As SerializationEntry In info
            dict.Add(entry.Name, DirectCast(entry.Value, String))
        Next
    End Sub
    Public Sub GetObjectData(info As SerializationInfo, context As StreamingContext) Implements ISerializable.GetObjectData
        For Each key As String in dict.Keys
            info.AddValue(key, dict.Item(key))
        Next
    End Sub
End Class

Appelé avec:

string MyJsonString = "{ \"key1\": \"value1\", \"key2\": \"value2\"}";
System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new
  System.Runtime.Serialization.Json.DataContractJsonSerializer(
    typeof(StringStringDictionary));
System.IO.MemoryStream ms = new
  System.IO.MemoryStream(Encoding.UTF8.GetBytes(MyJsonString));
StringStringDictionary myfields = (StringStringDictionary)dcjs.ReadObject(ms);
Response.Write("Value of key2: " + myfields.dict["key2"]);

Désolé pour le mélange de C # et VB.NET…


2
[TestMethod] public void TestSimpleObject () {const string json = @ "{" "Name" ":" "Bob" "," "Age" ": 42}"; var dict = new JavaScriptSerializer (). DeserializeObject (json) as IDictionary <chaîne, objet>; Assert.IsNotNull (dict); Assert.IsTrue (dict.ContainsKey ("Name")); Assert.AreEqual ("Bob", dict ["Nom"]); Assert.IsTrue (dict.ContainsKey ("Age")); Assert.AreEqual (42, dict ["Age"]); }
Mark Rendle

1
C'est fantastique. Aide aux implémentations de service WCF qui s'interfacent à l'aide de JSON avec des clients basés sur un navigateur.
Anton

@Mark Rendle: Votre implémentation est tellement simple, et c'est la SEULE qui a fonctionné jusqu'à présent pour obtenir à la fois les résultats json de réussite et de codes d'erreur. J'ai essayé de nombreuses solutions, alors merci de l'avoir posté en commentaire. Ce devrait être la réponse.
Bryan

5

J'ai ajouté le code soumis par jSnake04 et Dasun ici. J'ai ajouté du code pour créer des listes d'objets à partir d' JArrayinstances. Il a une récursion bidirectionnelle mais comme il fonctionne sur un modèle d'arbre fini et fixe, il n'y a aucun risque de débordement de pile à moins que les données ne soient massives.

/// <summary>
/// Deserialize the given JSON string data (<paramref name="data"/>) into a
///   dictionary.
/// </summary>
/// <param name="data">JSON string.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(string data)
{
    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(data);

    return DeserializeData(values);
}

/// <summary>
/// Deserialize the given JSON object (<paramref name="data"/>) into a dictionary.
/// </summary>
/// <param name="data">JSON object.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(JObject data)
{
    var dict = data.ToObject<Dictionary<String, Object>>();

    return DeserializeData(dict);
}

/// <summary>
/// Deserialize any elements of the given data dictionary (<paramref name="data"/>) 
///   that are JSON object or JSON arrays into dictionaries or lists respectively.
/// </summary>
/// <param name="data">Data dictionary.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(IDictionary<string, object> data)
{
    foreach (var key in data.Keys.ToArray()) 
    {
        var value = data[key];

        if (value is JObject)
            data[key] = DeserializeData(value as JObject);

        if (value is JArray)
            data[key] = DeserializeData(value as JArray);
    }

    return data;
}

/// <summary>
/// Deserialize the given JSON array (<paramref name="data"/>) into a list.
/// </summary>
/// <param name="data">Data dictionary.</param>
/// <returns>Deserialized list.</returns>
private IList<Object> DeserializeData(JArray data)
{
    var list = data.ToObject<List<Object>>();

    for (int i = 0; i < list.Count; i++)
    {
        var value = list[i];

        if (value is JObject)
            list[i] = DeserializeData(value as JObject);

        if (value is JArray)
            list[i] = DeserializeData(value as JArray);
    }

    return list;
}

4

J'ai ajouté une vérification des valeurs nulles dans le JSON à l'autre réponse

J'ai eu le même problème alors j'ai écrit ça moi-même. Cette solution se différencie des autres réponses car elle peut désérialiser à plusieurs niveaux.

Envoyez simplement la chaîne json à la fonction deserializeToDictionary , elle renverra un objet non fortement typé Dictionary<string, object>.

private Dictionary<string, object> deserializeToDictionary(string jo)
{
    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
    var values2 = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> d in values)
    {
        if (d.Value != null && d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
        {
            values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
        }
        else
        {
            values2.Add(d.Key, d.Value);
        }
    }
    return values2;
}

Ex: Cela retournera l' Dictionary<string, object>objet d'une réponse Facebook JSON.

private void button1_Click(object sender, EventArgs e)
{
    string responsestring = "{\"id\":\"721055828\",\"name\":\"Dasun Sameera
        Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\",
        hometown: {id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",}}";
    Dictionary<string, object> values = deserializeToDictionary(responsestring);
}

Remarque: la ville natale se désérialise davantage en un Dictionary<string, object>objet.


1
+1 Comme je l'ai dit avec Dasun ci-dessus. Vous pouvez simplement vérifier si d.Value is JObject. Vous n'avez pas à passer par la réflexion pour vérifier les types. Et avec l' isopérateur, vous n'avez pas besoin de vérifier la valeur null. Elle retourne false si l'objet est nul.
Jordan

3

Il semble que toutes ces réponses ici supposent simplement que vous pouvez obtenir cette petite chaîne d'un objet plus gros ... pour les personnes qui cherchent simplement à déseréaliser un grand objet avec un tel dictionnaire quelque part dans le mappage, et qui utilisent le System.Runtime.Serialization.Jsonsystème DataContract, voici une solution:

Une réponse sur gis.stackexchange.com avait ce lien intéressant . J'ai dû le récupérer avec archive.org, mais il offre une solution à peu près parfaite: une IDataContractSurrogateclasse personnalisée dans laquelle vous implémentez exactement vos propres types. J'ai pu l'agrandir facilement.

J'ai cependant apporté un tas de changements. Étant donné que la source d'origine n'est plus disponible, je publierai l'intégralité du cours ici:

using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;

namespace JsonTools
{
    /// <summary>
    /// Allows using Dictionary&lt;String,String&gt; and Dictionary&lt;String,Boolean&gt; types, and any others you'd like to add.
    /// Source: https://web.archive.org/web/20100317222656/my6solutions.com/post/2009/06/30/DataContractSerializer-DataContractJsonSerializer-JavaScriptSerializer-XmlSerializer-for-serialization.aspx
    /// </summary>
    public class JsonSurrogate : IDataContractSurrogate
    {
        /// <summary>
        /// Deserialize an object with added support for the types defined in this class.
        /// </summary>
        /// <typeparam name="T">Contract class</typeparam>
        /// <param name="json">JSON String</param>
        /// <param name="encoding">Text encoding</param>
        /// <returns>The deserialized object of type T</returns>
        public static T Deserialize<T>(String json, Encoding encoding)
        {
            if (encoding == null)
                encoding = new UTF8Encoding(false);
            DataContractJsonSerializer deserializer = new DataContractJsonSerializer(
                typeof(T), new Type[0], int.MaxValue, true, new JsonSurrogate(), false);
            using (MemoryStream stream = new MemoryStream(encoding.GetBytes(json)))
            {
                T result = (T)deserializer.ReadObject(stream);
                return result;
            }
        }

        // make sure all values in this are classes implementing JsonSurrogateObject.
        private static Dictionary<Type, Type> KnownTypes = 
            new Dictionary<Type, Type>()
            {
                {typeof(Dictionary<String, String>), typeof(SSDictionary)},
                {typeof(Dictionary<String, Boolean>), typeof(SBDictionary)}
            };

        #region Implemented surrogate dictionary classes

        [Serializable]
        public class SSDictionary : SurrogateDictionary<String>
        {
            public SSDictionary() : base() {}
            protected SSDictionary (SerializationInfo info, StreamingContext context) : base(info, context) {}
        }
        [Serializable]
        public class SBDictionary : SurrogateDictionary<Boolean>
        {
            public SBDictionary() : base() {}
            protected SBDictionary (SerializationInfo info, StreamingContext context) : base(info, context) {}
        }

        #endregion

        /// <summary>Small interface to easily extract the final value from the object.</summary>
        public interface JsonSurrogateObject
        {
            Object DeserializedObject { get; }
        }

        /// <summary>
        /// Class for deserializing any simple dictionary types with a string as key.
        /// </summary>
        /// <typeparam name="T">Any simple type that will be deserialized correctly.</typeparam>
            [Serializable]
        public abstract class SurrogateDictionary<T> : ISerializable, JsonSurrogateObject
        {
            public Object DeserializedObject { get { return dict; } }
            private Dictionary<String, T> dict;

            public SurrogateDictionary()
            {
                dict = new Dictionary<String, T>();
            }

            // deserialize
            protected SurrogateDictionary(SerializationInfo info, StreamingContext context)
            {
                dict = new Dictionary<String, T>();
                foreach (SerializationEntry entry in info)
                {
                    // This cast will only work for base types, of course.
                    dict.Add(entry.Name, (T)entry.Value);
                }
            }
            // serialize
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                foreach (String key in dict.Keys)
                {
                    info.AddValue(key, dict[key]);
                }
            }

        }

        /// <summary>
            /// Uses the KnownTypes dictionary to get the surrogate classes.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public Type GetDataContractType(Type type)
        {
            Type returnType;
            if (KnownTypes.TryGetValue(type, out returnType))
            {
                return returnType;
            }
            return type;
        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        ///     Gets the object out of the surrogate datacontract object. This function is the reason all surrogate objects need to implement the JsonSurrogateObject class.
        /// </summary>
        /// <param name="obj">Result of the deserialization</param>
        /// <param name="targetType">Expected target type of the deserialization</param>
        /// <returns></returns>
        public object GetDeserializedObject(object obj, Type targetType)
        {
            if (obj is JsonSurrogateObject)
            {
                return ((JsonSurrogateObject)obj).DeserializedObject;
            }
            return obj;
        }

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null;
        }

        #region not implemented

        public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
        {
            throw new NotImplementedException();
        }

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            throw new NotImplementedException();
        }

        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
            throw new NotImplementedException();
        }

        public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

Pour ajouter de nouveaux types pris en charge à la classe, il vous suffit d'ajouter votre classe, de lui donner les bons constructeurs et fonctions (regardez SurrogateDictionaryun exemple), assurez-vous qu'elle hérite JsonSurrogateObjectet ajoutez son mappage de type au KnownTypesdictionnaire. Le SurrogateDictionary inclus peut servir de base pour tous les Dictionary<String,T>types où T est tout type qui désérialise correctement.

L'appeler est vraiment simple:

MyObjtype newObj = JsonSurrogate.Deserialize<MyObjtype>(jsonStr, encoding);

Notez que pour une raison quelconque, cette chose a du mal à utiliser des chaînes de clés qui contiennent des espaces; ils n'étaient tout simplement pas présents dans la liste finale. Peut-être est-ce simplement contre les spécifications JSON et l'API que j'appelais a été mal implémentée, pensez-vous; Je ne sais pas. Quoi qu'il en soit, j'ai résolu ce problème en les remplaçant par des regex par des traits de soulignement dans les données brutes json et en corrigeant le dictionnaire après la désérialisation.


Soit dit en passant, pour une raison particulière, Mono semble avoir du mal à gérer ce genre de choses ...
Nyerguds

Merci pour le partage, malheureusement cette solution ne prend pas en charge les types non primitifs, et il n'y a aucun moyen d'obtenir la valeur brute, vous pouvez donc la construire vous-même. Si j'enregistre mon type personnalisé dans KnownTypes et que je l'utilise dans le dictionnaire, il appelle d'abord le dictionnaire, je m'attendrais à ce qu'il commence à analyser de bas en haut des types les plus distants aux plus complexes.
Ivan Leonenko

Eh bien, la question ne portait que sur Dictionary<String,String>. Honnêtement, je n'ai jamais essayé de désérialiser des types complexes avec ce système.
Nyerguds

3

Sur la base des commentaires ci-dessus, essayezJsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json)

var json = @"{""key1"":1,""key2"":""value2"", ""object1"":{""property1"":""value1"",""property2"":[2,3,4,5,6,7]}}";
var parsedObject = JsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json);

semble fonctionner même pour des objets et des listes complexes.


1

Je viens de l'implémenter dans RestSharp . Ce message m'a été utile.

Outre le code dans le lien, voici mon code. J'obtiens maintenant un Dictionaryrésultat lorsque je fais quelque chose comme ceci:

var jsonClient = new RestClient(url.Host);
jsonClient.AddHandler("application/json", new DynamicJsonDeserializer());
var jsonRequest = new RestRequest(url.Query, Method.GET);
Dictionary<string, dynamic> response = jsonClient.Execute<JObject>(jsonRequest).Data.ToObject<Dictionary<string, dynamic>>();

Soyez conscient du type de JSON que vous attendez - dans mon cas, je récupérais un seul objet avec plusieurs propriétés. Dans le lien ci-joint, l'auteur récupérait une liste.


1

Mon approche se désérialise directement en IDictionary, sans JObject ou ExpandObject entre les deux. Le code utilise un convertisseur, qui est essentiellement copié à partir de la classe ExpandoObjectConverter trouvée dans le code source JSON.NET, mais en utilisant IDictionary au lieu de ExpandoObject.

Usage:

var settings = new JsonSerializerSettings()
{
    Converters = { new DictionaryConverter() },
};
var result = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, settings);

Code:

// based on ExpandoObjectConverter, but using arrays instead of IList, to behave similar to System.Web.Script.Serialization.JavaScriptSerializer
public class DictionaryConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return ReadValue(reader);
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(IDictionary<string, object>));
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    private object ReadValue(JsonReader reader)
    {
        while (reader.TokenType == JsonToken.Comment)
        {
            if (!reader.Read())
                throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
        }

        switch (reader.TokenType)
        {
            case JsonToken.StartObject:
                return ReadObject(reader);
            case JsonToken.StartArray:
                return ReadList(reader);
            default:
                if (IsPrimitiveToken(reader.TokenType))
                    return reader.Value;

                throw JsonSerializationExceptionCreate(reader, string.Format(CultureInfo.InvariantCulture, "Unexpected token when converting IDictionary<string, object>: {0}", reader.TokenType));
        }
    }

    private object ReadList(JsonReader reader)
    {
        List<object> list = new List<object>();

        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.Comment:
                    break;
                default:
                    object v = ReadValue(reader);

                    list.Add(v);
                    break;
                case JsonToken.EndArray:
                    return list;
            }
        }

        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
    }

    private object ReadObject(JsonReader reader)
    {
        IDictionary<string, object> dictionary = new Dictionary<string, object>();
        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.PropertyName:
                    string propertyName = reader.Value.ToString();

                    if (!reader.Read())
                        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");

                    object v = ReadValue(reader);

                    dictionary[propertyName] = v;
                    break;
                case JsonToken.Comment:
                    break;
                case JsonToken.EndObject:
                    return dictionary;
            }
        }

        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
    }

    //based on internal Newtonsoft.Json.JsonReader.IsPrimitiveToken
    internal static bool IsPrimitiveToken(JsonToken token)
    {
        switch (token)
        {
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Undefined:
            case JsonToken.Null:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return true;
            default:
                return false;
        }
    }

    // based on internal Newtonsoft.Json.JsonSerializationException.Create
    private static JsonSerializationException JsonSerializationExceptionCreate(JsonReader reader, string message, Exception ex = null)
    {
        return JsonSerializationExceptionCreate(reader as IJsonLineInfo, reader.Path, message, ex);
    }

    // based on internal Newtonsoft.Json.JsonSerializationException.Create
    private static JsonSerializationException JsonSerializationExceptionCreate(IJsonLineInfo lineInfo, string path, string message, Exception ex)
    {
        message = JsonPositionFormatMessage(lineInfo, path, message);

        return new JsonSerializationException(message, ex);
    }

    // based on internal Newtonsoft.Json.JsonPosition.FormatMessage
    internal static string JsonPositionFormatMessage(IJsonLineInfo lineInfo, string path, string message)
    {
        if (!message.EndsWith(Environment.NewLine))
        {
            message = message.Trim();

            if (!message.EndsWith(".", StringComparison.Ordinal))
                message += ".";

            message += " ";
        }

        message += string.Format(CultureInfo.InvariantCulture, "Path '{0}'", path);

        if (lineInfo != null && lineInfo.HasLineInfo())
            message += string.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition);

        message += ".";

        return message;
    }
}

1

Vous pouvez utiliser Tiny-JSON

string json = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
IDictionary<string, string> dict = Tiny.Json.Decode<Dictionary<string, string>>(json);

1

Un peu tard dans le jeu, mais aucune des solutions ci-dessus ne m'a orienté vers une solution .NET pure et simple, pas de solution json.net. Le voici donc, a fini par être très simple. Ci-dessous un exemple complet de la façon dont cela est fait avec la sérialisation JSON .NET standard, l'exemple a un dictionnaire à la fois dans l'objet racine et dans les objets enfants.

La balle dorée est ce chat, analysez les paramètres comme deuxième paramètre du sérialiseur:

DataContractJsonSerializerSettings settings =
                       new DataContractJsonSerializerSettings();
                    settings.UseSimpleDictionaryFormat = true;

Code complet ci-dessous:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

    namespace Kipon.dk
    {
        public class JsonTest
        {
            public const string EXAMPLE = @"{
                ""id"": ""some id"",
                ""children"": {
                ""f1"": {
                    ""name"": ""name 1"",
                    ""subs"": {
                    ""1"": { ""name"": ""first sub"" },
                    ""2"": { ""name"": ""second sub"" }
                    }
                },
                ""f2"": {
                    ""name"": ""name 2"",
                    ""subs"": {
                    ""37"": { ""name"":  ""is 37 in key""}
                    }
                }
                }
            }
            ";

            [DataContract]
            public class Root
            {
                [DataMember(Name ="id")]
                public string Id { get; set; }

                [DataMember(Name = "children")]
                public Dictionary<string,Child> Children { get; set; }
            }

            [DataContract]
            public class Child
            {
                [DataMember(Name = "name")]
                public string Name { get; set; }

                [DataMember(Name = "subs")]
                public Dictionary<int, Sub> Subs { get; set; }
            }

            [DataContract]
            public class Sub
            {
                [DataMember(Name = "name")]
                public string Name { get; set; }
            }

            public static void Test()
            {
                var array = System.Text.Encoding.UTF8.GetBytes(EXAMPLE);
                using (var mem = new System.IO.MemoryStream(array))
                {
                    mem.Seek(0, System.IO.SeekOrigin.Begin);
                    DataContractJsonSerializerSettings settings =
                       new DataContractJsonSerializerSettings();
                    settings.UseSimpleDictionaryFormat = true;

                    var ser = new DataContractJsonSerializer(typeof(Root), settings);
                    var data = (Root)ser.ReadObject(mem);
                    Console.WriteLine(data.Id);
                    foreach (var childKey in data.Children.Keys)
                    {
                        var child = data.Children[childKey];
                        Console.WriteLine(" Child: " + childKey + " " + child.Name);
                        foreach (var subKey in child.Subs.Keys)
                        {
                            var sub = child.Subs[subKey];
                            Console.WriteLine("   Sub: " + subKey + " " + sub.Name);
                        }
                    }
                }
            }
        }
    }

0

Assez ennuyeux, si vous souhaitez utiliser les classeurs de modèle par défaut, il semble que vous devrez utiliser des valeurs d'index numériques comme un formulaire POST.

Voir l'extrait suivant de cet article http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Bien que cela soit quelque peu contre-intuitif, les demandes JSON ont les mêmes exigences: elles doivent également respecter la syntaxe de post-nommage du formulaire. Prenons, par exemple, la charge utile JSON pour la collection UnitPrice précédente. La syntaxe du tableau JSON pur pour ces données serait représentée comme suit:

[ 
  { "Code": "USD", "Amount": 100.00 },
  { "Code": "EUR", "Amount": 73.64 }
]

Cependant, les fournisseurs de valeurs par défaut et les classeurs de modèle nécessitent que les données soient représentées sous la forme d'un post de formulaire JSON:

{
  "UnitPrice[0].Code": "USD",
  "UnitPrice[0].Amount": 100.00,

  "UnitPrice[1].Code": "EUR",
  "UnitPrice[1].Amount": 73.64
}

Le scénario de collecte d'objets complexes est peut-être l'un des scénarios les plus problématiques que rencontrent les développeurs car la syntaxe n'est pas nécessairement évidente pour tous les développeurs. Cependant, une fois que vous avez appris la syntaxe relativement simple pour publier des collections complexes, ces scénarios deviennent beaucoup plus faciles à gérer.


0

Je suggère d'utiliser System.Runtime.Serialization.Jsoncela fait partie de .NET 4.5.

[DataContract]
public class Foo
{
   [DataMember(Name = "data")]
   public Dictionary<string,string> Data { get; set; }
}

Ensuite, utilisez-le comme ceci:

var serializer = new DataContractJsonSerializer(typeof(List<Foo>));
var jsonParams = @"{""data"": [{""Key"":""foo"",""Value"":""bar""}] }";
var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonParams));

var obj = serializer.ReadObject(stream);
Console.WriteLine(obj);

Où le sérialiseur est-il défini?
bnieland

..et qu'est-ce qu'un Category3MeasureModel? Aucun succès sur Google.
bnieland

1
C'est juste la classe de modèle que je sérialise pour mon projet. C'est censé être cette classe Foo, mais j'ai recopié toute la section du code de production. Vous devriez créer le vôtre, comme ma classe Foo. Je l'ai renommé Foo pour le rendre plus simple. Il s'agit simplement d'une classe de propriétés ou de champs que vous souhaitez sérialiser vers json et inversement.
Dan Csharpster

1
@DanCsharpster Avec un copier-coller exact de votre code, j'obtiens, sur Windows Phone 8.1 Silverlight: `Une exception de type 'System.Security.SecurityException' s'est produite dans System.ServiceModel.Web.ni.dll mais n'a pas été gérée par l'utilisateur code Informations supplémentaires: Le type de contrat de données 'MyApp.Foo' ne peut pas être désérialisé car le membre 'Data' n'est pas public. Rendre le membre public corrigera cette erreur. Alternativement, vous pouvez le rendre interne et utiliser l'attribut InternalsVisibleToAttribute sur votre assembly afin de permettre la sérialisation des membres internes
Cœur

1
@DanCsharpster Et lors de la modification de la propriété Data pour être membre (sans get; set;), j'obtiens: Une première exception de type "System.ArgumentException" s'est produite dans System.ServiceModel.Web.ni.dll Informations supplémentaires: objet de le type 'System.Object' ne peut pas être converti en type 'System.Collections.Generic.Dictionary`2 [System.String, System.String]'.
Cœur

0

Pour toute personne qui essaie de convertir JSON en dictionnaire juste pour en extraire une valeur. il existe un moyen simple d' utiliserNewtongsoft.JSON

using Newtonsoft.Json.Linq
...

JObject o = JObject.Parse(@"{
  'CPU': 'Intel',
  'Drives': [
    'DVD read/writer',
    '500 gigabyte hard drive'
  ]
}");

string cpu = (string)o["CPU"];
// Intel

string firstDrive = (string)o["Drives"][0];
// DVD read/writer

IList<string> allDrives = o["Drives"].Select(t => (string)t).ToList();
// DVD read/writer
// 500 gigabyte hard drive
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.