Wie kann ich JSON in ASP.NET in ein einfaches Dictionary <string, string> deserialisieren?


679

Ich habe eine einfache Schlüssel- / Werteliste in JSON, die per POST an ASP.NET zurückgesendet wird. Beispiel:

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

Ich versuche nicht, in stark typisierte .NET-Objekte zu deserialisieren

Ich brauche einfach ein einfaches altes Wörterbuch (Of String, String) oder ein gleichwertiges (Hash-Tabelle, Dictionary (Of String, Object)) StringDictionary der alten Schule - verdammt, ein 2-D-Array von Strings würde für mich funktionieren.

Ich kann alles verwenden, was in ASP.NET 3.5 verfügbar ist, sowie das beliebte Json.NET (das ich bereits für die Serialisierung auf dem Client verwende).

Anscheinend verfügt keine dieser JSON-Bibliotheken über diese offensichtliche Fähigkeit, die Stirn zu schlagen - sie konzentrieren sich vollständig auf die reflexionsbasierte Deserialisierung über starke Verträge.

Irgendwelche Ideen?

Einschränkungen:

  1. Ich möchte keinen eigenen JSON-Parser implementieren
  2. ASP.NET 4.0 kann noch nicht verwendet werden
  3. Ich würde es vorziehen, mich von der älteren, veralteten ASP.NET-Klasse für JSON fernzuhalten

1
re: Einschränkung 3 JavaScriptSerizlizerwird in ASP.NET MVC verwendet und ist nicht mehr veraltet.
Bdukes

17
Es ist unglaublich, wie schwierig es war, einen einfachen Weg zu finden, um einen JSON-String in etwas umzuwandeln, das ich leicht verwenden konnte, ohne durch viele verschiedene Stapelüberläufe zu blättern. In anderen Sprachen ist es so einfach, aber Java und C # scheinen sich alle Mühe zu geben, um das Leben zu erschweren.
user299709

Antworten:


891

Json.NET macht das ...

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

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

Weitere Beispiele: Serialisieren von Sammlungen mit Json.NET


9
Funktioniert dies auch, wenn Ihre Werte Ganzzahlen sind? Werden sie automatisch in "Strings" umgewandelt?
Highmastdon

58
@ Highmastdon Nein, tut es nicht. Ich habe den besten Weg gefunden, um in ein Wörterbuch zu deserialisieren, dynamicindem ich als Typ für die Werte verwende:JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
Erik Schierboom

1
Ich habe mehrere Antworten auf dieser Seite mit einem sehr unordentlichen Schlüssel / Wert-Paar versucht, und JSON.NET war das einzige, das ich ausprobiert habe, das funktioniert hat.
Bnieland

15
Funktioniert nicht, wenn Sie ein Array von Schlüsselwertpaaren in JSON verwenden. [{key: "a", value: "1"}, {key: "b", value:"2"}]Sie müssen Folgendes tun:var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
Adrian

8
Funktioniert auch nicht, wenn die Werte verschachtelte Objekte sind, da json.net diese als JObjects erstellt
Kugel

100

Ich habe festgestellt, dass .NET über eine integrierte Methode verfügt, um die JSON-Zeichenfolge in einen Dictionary<String, Object>Via- System.Web.Script.Serialization.JavaScriptSerializerTyp in der 3.5- System.Web.ExtensionsAssembly umzuwandeln. Verwenden Sie die Methode DeserializeObject(String).

Ich bin darauf gestoßen, als ich einen Ajax-Beitrag (über jquery) vom Inhaltstyp 'application / json' zu einer statischen .net-Seitenmethode verfasst habe, und habe festgestellt, dass die Methode (die einen einzelnen Parameter vom Typ hatte Object) dieses Wörterbuch auf magische Weise erhalten hat.


5
Aber der eingebaute Javascriptserializer ist fehlerhafter als json.net, diese Lösung ist besser. Beispielsweise gibt der javascriptseralizer Nullen anstelle von leeren Zeichenfolgen zurück und funktioniert bei nullbaren Eigenschaften usw. überhaupt nicht.
Pilavdzice

1
@pilavdzice Ganz zu schweigen von dem Spaß, den Sie beim Parsen von Daten haben, da das nicht standardmäßige Datumsformat von MS angenommen wird.
Basic

16
Schnelles Codebeispiel: var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();gefolgt vonDictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Nate Cook

6
Der Vorteil des Beispiels von Nate Cook in einem einfachen Fall besteht darin, die Notwendigkeit externer DLLs zu vermeiden. Ich greife über eine eigenständige Konsole auf eine API zu, die sich nur auf das .NET-Framework verlassen kann.
Nick.T

@pilavdzice Kannst du näher darauf eingehen? Ich kann das Ding "return null statt leere Zeichenfolgen" nicht reproduzieren, es gab mir einen Wert für leere Zeichenfolgen fürSomeData: ""
jrh

51

Für diejenigen, die im Internet suchen und auf diesen Beitrag stoßen, habe ich einen Blog-Beitrag über die Verwendung der JavaScriptSerializer-Klasse geschrieben.

Lesen Sie mehr ... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

Hier ist ein Beispiel:

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, ich habe deine Lösung ausprobiert ... Ich habe json wie diese {"id": "13", "value": true} und für mich funktioniert nur die Dictionary <dynamic> -Lösung
Marko

ok Ich habe es gefunden, wo das Problem liegt ... Sie müssen [] nach der Wörterbuchdeklaration hinzufügen, um richtig zu deserialisieren ... Ich füge auch Ihrem Blog-Beitrag einen Kommentar hinzu ... Prost;)
Marko

Ich habe meine Antwort aktualisiert, um Ihren spezifischen Datensatz wiederzugeben. Es funktioniert gut mit Dynamik.
JP Richardson

Ich habe gerade einen anderen JSON-Parser geschrieben, der etwas flexibler ist und Silverlight unterstützt: procbits.com/2011/08/11/…
JP Richardson

41

Ich habe versucht, keine externe JSON-Implementierung zu verwenden, daher habe ich Folgendes deserialisiert:

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
Fügen Sie die Referenz System.Web.Extensions hinzu, um System.Web.Script zu verwenden
Patrick Cullen

1
Diese Antwort gefällt mir am besten, weil sie einfach ist und .NET verwendet System.Web.Script.Serialization. Es funktioniert einfach. Ich konnte sogar "ungültige" JSON-ähnliche verwenden string json = "{'id':13, 'value': true}";.
Styfle

Gibt es aus Neugier dieselbe einzeilige Möglichkeit, in das OrdinalIgnoreCase-Wörterbuch zu deserialisieren?
Batbaatar

38

Ich hatte das gleiche Problem, also habe ich das selbst geschrieben. Diese Lösung unterscheidet sich von anderen Antworten, da sie auf mehrere Ebenen deserialisiert werden kann.

Senden Sie einfach eine JSON-Zeichenfolge an die Funktion deserializeToDictionary , um ein nicht stark typisiertes Dictionary<string, object>Objekt zurückzugeben.

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

Beispiel: Dies gibt das Dictionary<string, object>Objekt einer Facebook-JSON-Antwort zurück.

Prüfung

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

Hinweis: Heimatstadt deserilisieren weiter in ein Dictionary<string, object> Objekt.

Aktualisieren

Meine alte Antwort funktioniert hervorragend, wenn die JSON-Zeichenfolge kein Array enthält. Dieser deserialisiert weiter in ein, List<object>wenn ein Element ein Array ist.

Senden Sie einfach eine JSON-Zeichenfolge an die Funktion deserializeToDictionaryOrList , die ein nicht stark typisiertes Dictionary<string, object>Objekt oder zurückgibt 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 danke für den Hinweis, ich habe einige Änderungen an diesem Code vorgenommen, aber ich habe es jetzt nicht. Dieser Code verarbeitet keine JArray-Objekte. Ich werde den Code aktualisieren, sobald ich ihn habe.
Dasun

1
Kein Problem. Ich erwähne es nur, weil mir das Lernen über isund asOperatoren sehr geholfen und meinen eigenen Code vereinfacht hat.
Jordanien

Es funktioniert, ist aber nicht effizient, da es ToString aufruft und dann erneut deserialisiert. Schauen Sie sich Falkos Antwort unten an. Die Quellzeichenfolge wird nur einmal deserialisiert.
Sergei Zinovyev

1
Die Antwort von Falko funktioniert nur, wenn Sie die Datenstruktur im Voraus kennen. Diese Lösung kann für jede JSON-Zeichenfolge verwendet werden.
Dasun

16

Wenn Sie nach einem leichten Ansatz suchen, bei dem keine Referenzen hinzugefügt werden, funktioniert möglicherweise dieser Code, den ich gerade geschrieben habe (ich kann jedoch keine 100% ige Robustheit garantieren).

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

[Mir ist klar, dass dies gegen die OP-Einschränkung Nr. 1 verstößt, aber technisch gesehen haben Sie es nicht geschrieben, ich habe es getan]


3
Das ist die einzige Antwort, die für Silverlight und ohne Abhängigkeit funktioniert! Silverlight verfügt weder über JavascriptSerializer noch über Serializable. Und keine Abhängigkeit bedeutet kein Json.NET, RestSharp oder MiniJSON. Nur @DanCsharpster hat eine andere mögliche Lösung ausprobiert, aber leider hat es bei mir nicht so funktioniert wie diese.
Cœur

1
Was ist falsch daran, einen Verweis auf etwas Einfaches wie JSON.NET hinzuzufügen? Muss es so leicht sein, dass Sie nichts referenzieren können? Ich sage nicht, dass Ihr Code nicht funktioniert, aber jedes Mal, wenn Sie Ihren eigenen Code rollen, laufen Sie offensichtlich Gefahr, dass Ihr Code nicht so robust ist, beispielsweise für Randfälle oder schnell wie eine getestete Bibliothek wie JSON.NET.
Dan Csharpster

1
Wenn Sie eine gute Alternative haben, ist es eine schlechte Idee, sich selbst zu rollen. Ich kenne keine Situation , die sein muss , dass leicht. Und ich hätte lieber weniger optimalen Code, der leicht zu lesen und zu ändern ist.
Jordanien

3
Ich habe diesen Code ursprünglich geschrieben, weil ich keine Alternative hatte. Betrachten Sie Dinge wie Silverlight oder Anbieter verschiedener Typen für Office-Produkte, bei denen das Hinzufügen externer Verweise zum Projekt entweder äußerst problematisch oder unmöglich ist.
Dexy

Ich weiß, dass es einige Jahre später ist, aber dies ist immer noch eine sehr berechtigte Frage. Für alle, die sich fragen, warum wir so leicht werden möchten, wenn Sie mit SQL CLR C # arbeiten, gibt es nur so viele "sichere" Bibliotheken, die Sie verwenden können und System.RunTime.Serializationdie keine davon sind. Leider hängt JSON.NET davon ab es und somit kann es auch nicht verwendet werden. Vielen Dank an dexy für Ihre hervorragende Arbeit. Ich habe es gewagt, sie ein wenig zu verbessern, um Arrays von Arrays deserialisieren zu können. Den aktualisierten Code finden Sie in meiner Antwort hier .
Alberto Rechy

15

Ich musste nur ein verschachteltes Wörterbuch analysieren , wie

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

wo JsonConvert.DeserializeObjecthilft nicht. Ich habe folgenden Ansatz gefunden:

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

Mit SelectTokenkönnen Sie zum gewünschten Feld graben. Sie können sogar einen Pfad angeben "x.y.z", der weiter nach unten in das JSON-Objekt führen soll.


JObject.Parse (json) .ToObject <Dictionary <Guid, List <int> >> () hat in meinem Szenario für mich funktioniert, danke
geedubb

11

System.Text.Json

Dies kann jetzt mit System.Text.Jsonder eingebauten Funktion erfolgen .net core 3.0. Es ist jetzt möglich, JSON zu deserialisieren, ohne Bibliotheken von Drittanbietern zu verwenden.

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

Auch im nu-get-Paket System.Text.Json verfügbar, wenn .Net Standard oder .Net Framework verwendet werden.


1
Ja! System.Text.Jsonist heutzutage der richtige Weg.
mfluehr

2
Ja, es sieht vielversprechend aus! Beachten Sie jedoch, dass die Standardversion von System.Text.Json von .NET Core 3.1 das Deserialisieren von Wörterbüchern mit Schlüsseln ohne Zeichenfolge nicht unterstützt. Während es in meinem OP um Strings ging, habe ich in der Praxis jetzt viele Guid-Tasten, also "biss" mich das, wenn ich versuche, den Wechsel vorzunehmen. Es gibt auch keine Entsprechungen zu einigen Attributen (erforderlich usw.).
richardtallent

6

Mark Rendle hat dies als Kommentar gepostet. Ich wollte es als Antwort posten, da es die einzige Lösung ist, die bisher funktioniert hat, um den Erfolg und die Fehlercodes zurückzugeben, die json aus der Antwort von Google reCaptcha resultiert.

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

Nochmals vielen Dank, Mark!


1
Der JavaScriptSerializer ist fast veraltet. Die Dokumentation besagt, dass wir JSON.NET ( docs.microsoft.com/en-us/dotnet/api/… ) verwenden sollten
Mario Lopez

Auch gut für ältere Webforms-Apps geeignet, bei denen Sie keine zusätzlichen Abhängigkeiten einbeziehen möchten.
Andrew Grothe

5

Bearbeiten: Dies funktioniert, aber die akzeptierte Antwort mit Json.NET ist viel einfacher. Lassen Sie diesen, falls jemand nur BCL-Code benötigt.

Es wird vom .NET Framework nicht sofort unterstützt. Ein grelles Versehen - nicht jeder muss in Objekte mit benannten Eigenschaften deserialisieren. Also rollte ich meine eigenen:

<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

Angerufen mit:

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

Entschuldigung für die Mischung aus C # und VB.NET…


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

1
Das ist fantastisch. Hilft bei WCF-Service-Implementierungen, die über JSON mit browserbasierten Clients zusammenarbeiten.
Anton

@ Mark Rendle: Ihre Implementierung ist sooo einfach und die EINZIGE, die bisher für mich funktioniert hat, um sowohl die Erfolgs- als auch die Fehlercodes zu erhalten. Ich habe viele Lösungen ausprobiert. Vielen Dank, dass Sie dies als Kommentar veröffentlicht haben. Es sollte die Antwort sein.
Bryan

5

Ich habe den von jSnake04 und Dasun hier eingereichten Code hinzugefügt. Ich habe Code hinzugefügt, um Listen von Objekten aus JArrayInstanzen zu erstellen . Es gibt eine bidirektionale Rekursion, aber da es auf einem festen, endlichen Baummodell funktioniert, besteht kein Risiko eines Stapelüberlaufs, es sei denn, die Daten sind massiv.

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

Ich habe der anderen Antwort eine Prüfung auf Nullwerte im JSON hinzugefügt

Ich hatte das gleiche Problem, also schrieb ich das selbst. Diese Lösung unterscheidet sich von anderen Antworten, da sie auf mehrere Ebenen deserialisiert werden kann.

Senden Sie einfach einen JSON-String an die Funktion deserializeToDictionary , um ein nicht stark typisiertes Dictionary<string, object>Objekt zurückzugeben.

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

Beispiel: Dies gibt das Dictionary<string, object>Objekt einer Facebook-JSON-Antwort zurück.

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

Hinweis: Die Heimatstadt wird weiter in ein Dictionary<string, object>Objekt deserialisiert .


1
+1 Wie ich oben mit Dasun sagte. Sie könnten einfach prüfen, ob d.Value is JObject. Sie müssen nicht durch Reflexion gehen, um Typen zu überprüfen. Und mit dem isOperator müssen Sie nicht nach Null suchen. Es gibt false zurück, wenn das Objekt null ist.
Jordanien

3

Es scheint, dass all diese Antworten hier nur davon ausgehen, dass Sie diese kleine Zeichenfolge aus einem größeren Objekt herausholen können ... für Leute, die einfach ein großes Objekt mit einem solchen Wörterbuch irgendwo in der Zuordnung deserealisieren möchten und das System.Runtime.Serialization.JsonDataContract-System verwenden, hier eine Lösung:

Eine Antwort auf gis.stackexchange.com hatte diesen interessanten Link . Ich musste es mit archive.org wiederherstellen, aber es bietet eine ziemlich perfekte Lösung: eine benutzerdefinierte IDataContractSurrogateKlasse, in der Sie genau Ihre eigenen Typen implementieren. Ich konnte es leicht erweitern.

Ich habe jedoch einige Änderungen vorgenommen. Da die Originalquelle nicht mehr verfügbar ist, werde ich die gesamte Klasse hier posten:

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

Um der Klasse neue unterstützte Typen hinzuzufügen, müssen Sie nur Ihre Klasse hinzufügen, ihr die richtigen Konstruktoren und Funktionen zuweisen (siehe SurrogateDictionaryein Beispiel), sicherstellen, dass sie erbt JsonSurrogateObject, und ihre Typzuordnung zum KnownTypesWörterbuch hinzufügen . Das mitgelieferte SurrogateDictionary kann als Grundlage für alle Dictionary<String,T>Typen dienen, wobei T ein beliebiger Typ ist, der korrekt deserialisiert.

Es zu nennen ist wirklich einfach:

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

Beachten Sie, dass dieses Ding aus irgendeinem Grund Probleme mit der Verwendung von Schlüsselzeichenfolgen hat, die Leerzeichen enthalten. Sie waren einfach nicht in der endgültigen Liste vorhanden. Könnte einfach sein, dass es einfach gegen die JSON-Spezifikationen ist und die API, die ich anrief, schlecht implementiert wurde, wohlgemerkt; Ich weiß nicht. Wie auch immer, ich habe dies gelöst, indem ich sie durch Regex durch Unterstriche in den Json-Rohdaten ersetzt und das Wörterbuch nach der Deserialisierung repariert habe.


Übrigens, aus irgendeinem besonderen Grund scheint Mono Probleme zu haben, dieses Zeug zu betreiben ...
Nyerguds

Vielen Dank für die Freigabe. Leider unterstützt diese Lösung keine nicht-primitiven Typen und es gibt keine Möglichkeit, den Rohwert zu ermitteln, sodass Sie ihn selbst erstellen können. Wenn ich meinen benutzerdefinierten Typ in KnownTypes registriere und ihn im Wörterbuch verwende, ruft er zuerst das Wörterbuch auf. Ich würde erwarten, dass er von den entferntesten bis zu den komplexeren Typen von unten nach oben analysiert.
Ivan Leonenko

Nun, die Frage wurde nur gestellt Dictionary<String,String>. Ich habe ehrlich gesagt nie versucht, komplexe Typen mit diesem System zu deserialisieren.
Nyerguds

3

Basierend auf den obigen Kommentaren versuchen Sie esJsonConvert.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);

scheint sogar für komplexe Objekte und Listen zu funktionieren.


1

Ich habe dies gerade in RestSharp implementiert . Dieser Beitrag war hilfreich für mich.

Neben dem Code im Link ist hier mein Code. Ich bekomme jetzt eine Reihe Dictionaryvon Ergebnissen, wenn ich so etwas mache:

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

Denken Sie an die Art von JSON, die Sie erwarten - in meinem Fall habe ich ein einzelnes Objekt mit mehreren Eigenschaften abgerufen. Im angehängten Link hat der Autor eine Liste abgerufen.


1

Mein Ansatz deserialisiert direkt auf IDictionary, ohne dazwischen JObject oder ExpandObject. Der Code verwendet den Konverter, der im Wesentlichen aus der im JSON.NET-Quellcode enthaltenen ExpandoObjectConverter-Klasse kopiert wird, jedoch IDictionary anstelle von ExpandoObject verwendet.

Verwendungszweck:

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

Sie könnten Tiny-JSON verwenden

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

1

Ein bisschen spät zum Spiel, aber keine der oben genannten Lösungen hat mich in Richtung einer reinen und einfachen .NET-Lösung geführt, keine json.net-Lösung. Also hier ist es, am Ende sehr einfach. Im Folgenden finden Sie ein ausführliches Beispiel für die Standard-.NET Json-Serialisierung. Das Beispiel enthält ein Wörterbuch sowohl im Stammobjekt als auch in den untergeordneten Objekten.

Die goldene Kugel ist diese Katze. Analysieren Sie die Einstellungen als zweiten Parameter für den Serializer:

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

Vollständiger Code unten:

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

Ärgerlicherweise müssen Sie, wenn Sie die Standardmodellbinder verwenden möchten, numerische Indexwerte wie einen Formular-POST verwenden.

Siehe den folgenden Auszug aus diesem Artikel http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Obwohl dies nicht intuitiv ist, haben JSON-Anforderungen dieselben Anforderungen - auch sie müssen sich an die Syntax für die Benennung von Formularen halten. Nehmen Sie zum Beispiel die JSON-Nutzdaten für die vorherige UnitPrice-Sammlung. Die reine JSON-Array-Syntax für diese Daten würde wie folgt dargestellt:

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

Bei den Standardwertanbietern und Modellbindern müssen die Daten jedoch als JSON-Formularbeitrag dargestellt werden:

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

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

Das komplexe Objektsammlungsszenario ist möglicherweise eines der problematischsten Szenarien, auf die Entwickler stoßen, da die Syntax nicht unbedingt für alle Entwickler offensichtlich ist. Sobald Sie jedoch die relativ einfache Syntax zum Posten komplexer Sammlungen kennen, werden diese Szenarien viel einfacher zu handhaben.


0

Ich würde vorschlagen, System.Runtime.Serialization.Jsondass dies Teil von .NET 4.5 ist.

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

Dann benutze es so:

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

Wo ist der Serializer definiert?
Bnieland

..und was ist ein Category3MeasureModel? Keine Treffer bei Google.
Bnieland

1
Das ist nur die Modellklasse, die ich für mein Projekt serialisiere. Es soll diese Foo-Klasse sein, aber ich habe den gesamten Abschnitt aus dem Produktionscode neu kopiert. Du solltest deine eigene erstellen, wie meine Foo-Klasse. Ich habe es in Foo umbenannt, um es einfacher zu machen. Es ist nur eine Klasse der Eigenschaften oder Felder, die Sie an json und zurück serialisieren möchten.
Dan Csharpster

1
@DanCsharpster Mit einer genauen Kopie Ihres Codes wird unter Windows Phone 8.1 Silverlight Folgendes angezeigt: "Eine Ausnahme vom Typ" System.Security.SecurityException "ist in System.ServiceModel.Web.ni.dll aufgetreten, wurde jedoch vom Benutzer nicht behandelt Code Zusätzliche Informationen: Der Datenvertragstyp 'MyApp.Foo' kann nicht deserialisiert werden, da das Mitglied 'Data' nicht öffentlich ist. Wenn Sie das Mitglied öffentlich machen, wird dieser Fehler behoben. Alternativ können Sie es intern machen und das Attribut InternalsVisibleToAttribute für Ihre Assembly verwenden, um die Serialisierung interner Mitglieder zu ermöglichen
Cœur

1
@DanCsharpster Und wenn ich die Eigenschaft Data als Mitglied ändere (ohne get; set;), erhalte ich: Eine erste zufällige Ausnahme vom Typ 'System.ArgumentException' ist in System.ServiceModel.Web.ni.dll aufgetreten. Zusätzliche Informationen: Objekt von Typ 'System.Object' kann nicht in Typ 'System.Collections.Generic.Dictionary`2 [System.String, System.String]' konvertiert werden.
Cœur

0

Für alle, die versuchen, JSON in ein Wörterbuch zu konvertieren, nur um einen Wert daraus zu ziehen. Es gibt einen einfachen Weg mitNewtongsoft.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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.