Come posso deserializzare JSON in un semplice dizionario <stringa, stringa> in ASP.NET?


682

Ho un semplice elenco chiave / valore in JSON che viene rispedito ad ASP.NET tramite POST. Esempio:

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

Non sto provando a deserializzare in oggetti .NET di tipo forte

Ho semplicemente bisogno di un semplice dizionario vecchio (di stringa, stringa) o di un equivalente (tabella hash, dizionario (di stringa, oggetto), StringDictionary della vecchia scuola - inferno, un array 2-D di stringhe funzionerebbe per me.

Posso usare qualsiasi cosa disponibile in ASP.NET 3.5, così come il popolare Json.NET (che sto già utilizzando per la serializzazione al client).

Apparentemente nessuna di queste librerie JSON ha questa ovvia capacità di schiacciare la fronte fuori dalla scatola - sono totalmente concentrate sulla deserializzazione basata sulla riflessione attraverso contratti forti.

Qualche idea?

limitazioni:

  1. Non voglio implementare il mio parser JSON personale
  2. Non è ancora possibile utilizzare ASP.NET 4.0
  3. Preferirei stare alla larga dalla classe ASP.NET obsoleta e obsoleta per JSON

1
re: limitation 3, JavaScriptSerizlizerviene utilizzato in ASP.NET MVC e non è più obsoleto.
bdukes

17
è incredibile quanto sia stato difficile trovare un modo semplice per convertire una stringa JSON in qualcosa che potrei facilmente usare senza sfogliare molti diversi stackoverflow. È così facile in altre lingue eppure Java e C # sembrano fare di tutto per rendere la vita difficile.
user299709

Risposte:


893

Json.NET fa questo ...

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

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

Altri esempi: serializzazione di raccolte con Json.NET


9
Funziona anche quando i tuoi valori sono numeri interi. Vengono automaticamente convertiti in "stringhe"?
Highmastdon,

58
@Highmastdon No, non lo è. Ho trovato il modo migliore per deserializzare in un dizionario è usare dynamiccome tipo per i valori:JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
Erik Schierboom,

1
Ho provato diverse risposte in questa pagina con una coppia chiave / valore molto disordinata e JSON.NET è stato l'unico a cui ho provato che ha funzionato.
bnieland,

15
Non funziona se stai usando un array di coppie chiave-valore in JSON [{key: "a", value: "1"}, {key: "b", value:"2"}]devi fare qualcosa del genere:var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
Adrian,

8
Inoltre, non funziona se i valori sono oggetti nidificati, perché json.net li crea come JObjects
Kugel,

100

Ho scoperto che .NET ha un modo integrato per eseguire il cast della stringa JSON in un tipo Dictionary<String, Object>tramite l' assembly System.Web.Script.Serialization.JavaScriptSerializer3.5 System.Web.Extensions. Utilizzare il metodo DeserializeObject(String).

Mi sono imbattuto in questo quando ho fatto un post ajax (tramite jquery) del tipo di contenuto 'application / json' in un metodo Page .net statico e ho visto che il metodo (che aveva un singolo parametro di tipo Object) riceveva magicamente questo dizionario.


5
ma il javascriptserializer integrato è più potente di json.net, questa soluzione è migliore. Ad esempio, javascriptseralizer restituirà valori null anziché stringhe vuote e non funziona affatto per le proprietà nullable e così via.
pilavdzice,

1
@pilavdzice Per non parlare del divertimento che provi quando cerchi di analizzare le date dato che assume il formato data non standard di MS.
Base

16
Esempio di codice rapido: var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();seguito daDictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Nate Cook il

6
Il vantaggio dell'esempio di Nate Cook in un semplice caso è quello di evitare la necessità di DLL esterne. Accedo a un'API da una console autonoma che può fare affidamento solo sul framework .Net.
Nick.T,

@pilavdzice Puoi approfondire questo? Non riesco a riprodurre la cosa "return null invece di stringhe vuote", mi ha dato un valore stringa vuota perSomeData: ""
jrh

51

Per coloro che cercano su Internet e si imbattono in questo post, ho scritto un post sul blog su come utilizzare la classe JavaScriptSerializer.

Leggi di più ... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

Ecco un esempio:

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, ho provato la tua soluzione ... Ho json come questo {"id": "13", "value": true} e per me funziona solo la soluzione <dynamic> del dizionario
Marko

ok l'ho trovato dov'è il problema ... devi aggiungere [] dopo la dichiarazione del dizionario per deserializzare correttamente ... sto aggiungendo anche un commento al tuo post sul blog ... evviva;)
Marko,

Ho aggiornato la mia risposta per riflettere il tuo set di dati specifico. Funziona bene con la dinamica.
JP Richardson,

Ho appena scritto un altro parser JSON che è un po 'più flessibile e supporta Silverlight: procbits.com/2011/08/11/…
JP Richardson,

41

Ho cercato di non utilizzare alcuna implementazione JSON esterna quindi ho deserializzato in questo modo:

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
Aggiungi riferimento System.Web.Extensions per utilizzare System.Web.Script
Patrick Cullen

1
Mi piace molto questa risposta perché è semplice e utilizza .NET System.Web.Script.Serialization. Funziona e basta. Sono stato anche in grado di utilizzare JSON "non valido" come string json = "{'id':13, 'value': true}";.
Styfle,

Per curiosità, esiste lo stesso modo in una sola riga per deserializzare il dizionario OrdinalIgnoreCase?
Batbaatar,

38

Ho avuto lo stesso problema, quindi ho scritto questo me stesso. Questa soluzione si differenzia dalle altre risposte perché può deserializzare a più livelli.

Basta inviare una stringa JSON alla funzione deserializeToDictionary che restituirà un oggetto non fortemente tipizzato Dictionary<string, object>.

Vecchio codice

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

Es: questo restituirà l' Dictionary<string, object>oggetto di una risposta JSON di Facebook.

Test

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

Nota: la città natale ulteriormente deserilizza in un Dictionary<string, object> oggetto.

Aggiornare

La mia vecchia risposta funziona alla grande se non ci sono array sulla stringa JSON. Questo deserializza ulteriormente in un List<object>se un elemento è un array.

Basta inviare una stringa JSON alla funzione deserializeToDictionaryOrList che restituirà oggetto non fortemente tipizzato Dictionary<string, object>o 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 grazie per aver sottolineato, ho apportato alcune modifiche a questo codice ma non ce l'ho adesso. Questo codice non gestisce gli oggetti JArray, aggiornerò il codice dopo averlo.
Dasun,

1
Non è un problema. Cito solo perché conoscere la ise asoperatori molto mi ha aiutato e semplificato il mio codice.
Giordania,

Funziona, ma non è efficiente, perché chiama ToString e poi Deserialize di nuovo. Guarda la risposta di Falko di seguito. Deserializza la stringa di origine solo una volta.
Sergei Zinovyev,

1
La risposta di Falko funziona solo se si conosce in anticipo la struttura dei dati. Questa soluzione può essere utilizzata per qualsiasi stringa JSON.
Dasun,

16

Se stai cercando un approccio leggero, senza riferimenti aggiunti, forse questo pezzetto di codice che ho appena scritto funzionerà (non posso garantire al 100% la robustezza).

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

[Mi rendo conto che ciò viola la limitazione n. 1 OP, ma tecnicamente non l'hai scritta tu, l'ho fatto]


3
Questa è l'unica risposta che funziona per Silverlight e senza dipendenza! Silverlight non ha JavascriptSerializer o Serializable. E nessuna dipendenza significa niente Json.NET, RestSharp o MiniJSON. Solo @DanCsharpster ha provato un'altra possibile soluzione, ma sfortunatamente non ha funzionato per me come fa questa.
Cu

1
Cosa c'è di sbagliato nell'aggiungere un riferimento a qualcosa di semplice come JSON.NET? Deve essere così leggero da non poter fare riferimento a nulla? Non sto dicendo che il tuo codice non funzionerà, ma ogni volta che esegui il tuo, corri ovviamente il rischio che il tuo codice non sia così robusto, per cose come casi limite o veloce come una libreria testata come JSON.NET.
Dan Csharpster,

1
Avere il tuo è una cattiva idea quando hai una buona alternativa. Non conosco alcuna situazione che debba essere così leggera. E preferirei avere un codice meno ottimale, facile da leggere e modificare.
Giordania,

3
Inizialmente ho scritto quel pezzo di codice perché non avevo alternative. Considerare cose come Silverlight o fornitori di vari tipi per prodotti Office, in cui l'aggiunta di riferimenti esterni al progetto è estremamente problematica o impossibile.
Dexy,

So che è qualche anno dopo, ma questa è ancora una domanda molto valida. Per chiunque si chieda perché dovremmo voler andare così leggeri, beh, se stai lavorando con SQL CLR C #, ci sono solo così tante librerie "sicure" che puoi usare e System.RunTime.Serializationnon è una di queste, sfortunatamente JSON.NET dipende da e quindi non può essere utilizzato neanche. Grazie a Dexy per il tuo eccellente lavoro, ho avuto il coraggio di migliorarlo un po 'per poter deserializzare le matrici di matrici, vedi il codice aggiornato nella mia risposta qui .
Alberto Rechy,

15

Avevo solo bisogno di analizzare un dizionario nidificato , come

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

dove JsonConvert.DeserializeObjectnon aiuta. Ho trovato il seguente approccio:

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

Il SelectTokenpermette di scavare fino al campo desiderato. È anche possibile specificare un percorso come "x.y.z"scendere ulteriormente nell'oggetto JSON.


JObject.Parse (json) .ToObject <Dizionario <Guid, Elenco <int> >> () ha funzionato per me nel mio scenario grazie
geedubb,

11

System.Text.Json

Questo ora può essere fatto usando il System.Text.Jsonquale è incorporato .net core 3.0. Ora è possibile deserializzare JSON senza utilizzare librerie di terze parti.

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

Disponibile anche nel pacchetto nu-get System.Text.Json se si utilizza .Net Standard o .Net Framework.


1
Sì! System.Text.Jsonè la strada da percorrere in questi giorni.
mfluehr,

2
Sì, sembra promettente! Tuttavia, tenere presente che la versione predefinita di System.Text.Json di .NET Core 3.1 non supporta la deserializzazione dei dizionari con chiavi non stringa. Mentre il mio OP riguardava le stringhe, in pratica ora, ho molte chiavi Guid, quindi questo mi ha "morso" quando provo a fare il cambio. Inoltre non ha equivalenti di alcuni degli attributi (richiesti, ecc.).
richardtallent,

6

Mark Rendle ha pubblicato questo post come commento , volevo pubblicarlo come risposta poiché è l'unica soluzione che ha funzionato finora per restituire il successo e i risultati dei codici di errore json dalla risposta di Google reCaptcha.

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

Grazie ancora, Mark!


1
JavaScriptSerializer è stato quasi deprecato. La documentazione dice che dovremmo usare JSON.NET ( docs.microsoft.com/en-us/dotnet/api/… )
Mario Lopez,

Buono anche per le app di moduli web legacy in cui non si desidera includere dipendenze aggiuntive.
Andrew Grothe,

5

Modifica: funziona, ma la risposta accettata utilizzando Json.NET è molto più semplice. Lasciare questo nel caso in cui qualcuno abbia bisogno del codice solo BCL.

Non è supportato dal framework .NET pronto all'uso. Una supervisione eclatante: non tutti hanno bisogno di deserializzare in oggetti con proprietà denominate. Quindi ho finito per farmi il mio:

<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

Chiamato con:

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"]);

Ci scusiamo per il mix di C # e VB.NET ...


2
[TestMethod] void pubblico TestSimpleObject () {const string json = @ "{" "Name" ":" "Bob" "," "Age" ": 42}"; var dict = new JavaScriptSerializer (). DeserializeObject (json) come IDictionary <stringa, oggetto>; Assert.IsNotNull (dict); Assert.IsTrue (dict.ContainsKey ( "Nome")); Assert.AreEqual ("Bob", dict ["Name"]); Assert.IsTrue (dict.ContainsKey ( "Age")); Assert.AreEqual (42, dict ["Age"]); }
Mark Rendle,

1
È fantastico. Aiuta con implementazioni di servizi WCF che si interfacciano usando JSON con client basati su browser.
Anton

@Mark Rendle: L'implementazione è talmente semplice, ed è l'UNICA che ha funzionato finora per me nel ottenere sia i risultati json di successo sia i codici di errore. Ho provato molte soluzioni, quindi grazie per averlo pubblicato come commento. Dovrebbe essere la risposta.
Bryan,

5

Ho aggiunto il codice inviato da jSnake04 e Dasun nel presente documento. Ho aggiunto codice per creare elenchi di oggetti dalle JArrayistanze. Ha una ricorsione a due vie ma poiché funziona su un modello ad albero fisso e finito, non c'è rischio di overflow dello stack a meno che i dati non siano enormi.

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

Ho aggiunto un controllo per i valori null in JSON all'altra risposta

Ho avuto lo stesso problema, quindi ho scritto questo me stesso. Questa soluzione si differenzia dalle altre risposte perché può deserializzare a più livelli.

Basta inviare una stringa json alla funzione deserializeToDictionary che restituirà un oggetto non fortemente tipizzato 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;
}

Es: questo restituirà l' Dictionary<string, object>oggetto di una risposta JSON di Facebook.

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

Nota: la città natale si deserializza ulteriormente in un Dictionary<string, object>oggetto.


1
+1 Come ho detto con Dasun sopra. Puoi solo verificare se d.Value is JObject. Non è necessario passare attraverso la riflessione per verificare i tipi. E con l' isoperatore non è necessario verificare la presenza di null. Restituisce false se l'oggetto è null.
Giordania,

3

Sembra che tutte queste risposte qui suppongano solo che puoi ottenere quella piccola stringa da un oggetto più grande ... per le persone che cercano semplicemente di deserealizzare un oggetto di grandi dimensioni con un dizionario del genere da qualche parte all'interno della mappatura e che stanno usando il System.Runtime.Serialization.Jsonsistema DataContract, ecco una soluzione:

Una risposta su gis.stackexchange.com aveva questo link interessante . Ho dovuto recuperarlo con archive.org, ma offre una soluzione praticamente perfetta: una IDataContractSurrogateclasse personalizzata in cui implementi esattamente i tuoi tipi. Sono stato in grado di espanderlo facilmente.

Tuttavia, ho apportato alcune modifiche. Poiché la fonte originale non è più disponibile, posterò l'intera classe qui:

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

Per aggiungere nuovi tipi supportati alla classe, devi solo aggiungere la tua classe, dargli i costruttori e le funzioni giusti (guarda SurrogateDictionaryun esempio), assicurarti che erediti JsonSurrogateObjecte aggiungere la sua mappatura dei tipi al KnownTypesdizionario. SurrogateDictionary incluso può fungere da base per qualsiasi Dictionary<String,T>tipo in cui T è qualsiasi tipo che si deserializzi correttamente.

Chiamarlo è davvero semplice:

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

Nota che per qualche ragione questa cosa ha problemi ad usare stringhe chiave che contengono spazi; semplicemente non erano presenti nella lista finale. Potrebbe essere semplicemente contro le specifiche JSON e l'API che stavo chiamando era mal implementata, intendiamoci; Non so. Comunque, ho risolto questo problema regex-sostituendoli con caratteri di sottolineatura nei dati json grezzi e riparando il dizionario dopo la deserializzazione.


A proposito, per qualche motivo particolare, Mono sembra avere problemi a gestire questa roba ...
Nyerguds

Grazie per la condivisione, purtroppo questa soluzione non supporta tipi non primitivi e non c'è modo di ottenere il valore grezzo, quindi puoi costruirlo tu stesso. Se registro il mio tipo personalizzato in KnownTypes e lo uso nel dizionario, chiama prima il dizionario, mi aspetto che inizi a analizzare dal basso verso l'alto dai tipi più remoti a quelli più complessi.
Ivan Leonenko,

Bene, la domanda è stata solo fatta Dictionary<String,String>. Onestamente non ho mai provato a deserializzare tipi complessi con questo sistema.
Nyerguds,

3

Sulla base dei commenti sopra, provaJsonConvert.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);

sembra funzionare anche per oggetti ed elenchi complessi.


1

Ho appena implementato questo in RestSharp . Questo post mi è stato utile.

Oltre al codice nel link, ecco il mio codice. Ora ottengo un Dictionaryrisultato quando faccio qualcosa del genere:

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

Fai attenzione al tipo di JSON che ti aspetti: nel mio caso, stavo recuperando un singolo oggetto con diverse proprietà. Nel link allegato, l'autore stava recuperando un elenco.


1

Il mio approccio si deserializza direttamente su IDictionary, senza JObject o ExpandObject nel mezzo. Il codice utilizza il convertitore, che viene sostanzialmente copiato dalla classe ExpandoObjectConverter presente nel codice sorgente JSON.NET, ma utilizzando IDictionary invece di ExpandoObject.

Uso:

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

Codice:

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

Puoi usare Tiny-JSON

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

1

Un po 'tardi al gioco, ma nessuna delle soluzioni di cui sopra mi ha indicato la direzione di un .NET puro e semplice, nessuna soluzione json.net. Quindi eccolo qui, finito per essere molto semplice. Di seguito un esempio completo di come viene eseguito con la serializzazione .NET Json standard, l'esempio ha un dizionario sia nell'oggetto root che negli oggetti figlio.

Il proiettile d'oro è questo gatto, analizza le impostazioni come secondo parametro per il serializzatore:

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

Codice completo di seguito:

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

Abbastanza fastidioso, se si desidera utilizzare i raccoglitori di modelli predefiniti, sembra che sarà necessario utilizzare valori di indice numerici come un modulo POST.

Vedere il seguente estratto di questo articolo http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Sebbene sia in qualche modo controintuitivo, le richieste JSON hanno gli stessi requisiti: anche loro devono aderire alla sintassi dei nomi post modulo. Prendi, ad esempio, il payload JSON per la precedente raccolta UnitPrice. La sintassi dell'array JSON pura per questi dati sarebbe rappresentata come:

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

Tuttavia, i provider di valori predefiniti e i raccoglitori di modelli richiedono che i dati siano rappresentati come un post del modulo JSON:

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

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

Il complesso scenario di raccolta di oggetti è forse uno degli scenari più ampiamente problematici che gli sviluppatori incontrano perché la sintassi non è necessariamente evidente per tutti gli sviluppatori. Tuttavia, una volta appresa la sintassi relativamente semplice per la pubblicazione di raccolte complesse, questi scenari diventano molto più facili da gestire.


0

Suggerirei di utilizzare System.Runtime.Serialization.Jsonquesto fa parte di .NET 4.5.

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

Quindi usalo in questo modo:

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

Dove viene definito il serializzatore?
bnieland,

..e che cos'è un Model3MeasureModel? Nessun risultato su Google.
bnieland,

1
Questa è solo la classe del modello che sto serializzando per il mio progetto. Dovrebbe essere quella classe Foo, ma ho ricopiato l'intera sezione dal codice di produzione. Dovresti crearne uno tuo, come la mia classe Foo. L'ho rinominato in Foo per renderlo più semplice. È solo una classe delle proprietà o dei campi che si desidera serializzare su json e viceversa.
Dan Csharpster,

1
@DanCsharpster Con una copia esatta del tuo codice, ottengo, su Windows Phone 8.1 Silverlight: `Si è verificata un'eccezione di tipo 'System.Security.SecurityException' in System.ServiceModel.Web.ni.dll ma non è stata gestita dall'utente codice Informazioni aggiuntive: il tipo di contratto dati "MyApp.Foo" non può essere deserializzato perché il membro "Dati" non è pubblico. Rendere il membro pubblico risolverà questo errore. In alternativa, è possibile renderlo interno e utilizzare l'attributo InternalsVisibleToAttribute sull'assembly per consentire la serializzazione dei membri interni
Cœur,

1
@DanCsharpster E quando si modifica la proprietà Data per essere un membro (senza get; set;), ottengo: un'eccezione della prima possibilità del tipo 'System.ArgumentException' si è verificata in System.ServiceModel.Web.ni.dll Ulteriori informazioni: Object of digitare 'System.Object' non può essere convertito nel tipo 'System.Collections.Generic.Dictionary`2 [System.String, System.String]'.
Cu

0

Per chiunque stia cercando di convertire JSON in dizionario solo per averne ricavato un valore. c'è un modo semplice usandoNewtongsoft.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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.