Bagaimana saya membatalkan deserialisasi JSON ke Kamus sederhana <string, string> di ASP.NET?

682

Saya memiliki daftar kunci / nilai sederhana di JSON yang dikirim kembali ke ASP.NET melalui POST. Contoh:

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

SAYA TIDAK BERCOBA UNTUK MENERBITKAN KE DALAM OBYEK .NET OBYEK

Saya hanya perlu Kamus tua sederhana (Dari String, String) , atau setara (tabel hash, Kamus (Of String, Object), old-school StringDictionary - hell, 2-D array string akan bekerja untuk saya.

Saya dapat menggunakan apa pun yang tersedia di ASP.NET 3.5, serta Json.NET yang populer (yang sudah saya gunakan untuk serialisasi ke klien).

Rupanya tak satu pun dari pustaka JSON ini yang memiliki kemampuan menampar dahi yang jelas di luar kotak - mereka benar-benar fokus pada deserialisasi berbasis refleksi melalui kontrak yang kuat.

Ada ide?

Keterbatasan:

  1. Saya tidak ingin mengimplementasikan parser JSON saya sendiri
  2. Belum dapat menggunakan ASP.NET 4.0
  3. Lebih suka tinggal jauh dari yang lebih tua, kelas ASP.NET usang untuk JSON
richardtallent
sumber
1
re: limitation 3, JavaScriptSerizlizerdigunakan dalam ASP.NET MVC dan tidak lagi ditinggalkan.
bdukes
17
Sungguh luar biasa betapa sulitnya menemukan cara sederhana untuk mengubah string json menjadi sesuatu yang dapat saya gunakan dengan mudah tanpa membalik-balik berbagai stackoverflow. Sangat mudah dalam bahasa lain, namun Java dan C # tampaknya berusaha keras untuk membuat hidup lebih sulit.
user299709

Jawaban:

893

Json.NET melakukan ini ...

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

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

Lebih banyak contoh: Serializing Collections dengan Json.NET

James Newton-King
sumber
9
Apakah ini juga berfungsi ketika nilai Anda bilangan bulat. Apakah mereka secara otomatis dilemparkan ke 'string'?
Highmastdon
58
@ Highmastdon Tidak, tidak. Saya telah menemukan cara terbaik untuk deserialize ke dalam kamus adalah dengan menggunakan dynamicsebagai nilai untuk nilai-nilai:JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
Erik Schierboom
1
Mencoba beberapa jawaban pada halaman ini dengan pasangan kunci / nilai yang sangat berantakan, dan JSON.NET adalah satu-satunya yang saya coba yang berfungsi.
bnieland
15
Tidak berfungsi jika Anda menggunakan array pasangan nilai kunci di json, [{key: "a", value: "1"}, {key: "b", value:"2"}]Anda harus melakukan sesuatu seperti ini:var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
Adrian
8
Juga tidak berfungsi jika nilainya adalah objek bersarang, karena json.net menciptakannya sebagai JObjects
Kugel
100

Saya menemukan .NET memiliki cara untuk memasukkan string JSON ke dalam Dictionary<String, Object>melalui System.Web.Script.Serialization.JavaScriptSerializertipe dalam System.Web.Extensionsperakitan 3,5 . Gunakan metodenya DeserializeObject(String).

Saya menemukan ini ketika melakukan posting ajax (via jquery) dari tipe konten 'application / json' ke .net Page Metode statis dan melihat bahwa metode (yang memiliki parameter tipe tunggal Object) secara ajaib menerima Kamus ini.

Renyah
sumber
5
tetapi built in javascriptserializer adalah buggier daripada json.net, solusi itu lebih baik. Misalnya javascriptseralizer akan mengembalikan nol alih-alih string kosong, dan tidak berfungsi sama sekali untuk properti yang dapat dibatalkan, dan sebagainya.
pilavdzice
1
@pilavdzice Belum lagi kesenangan yang Anda miliki ketika mencoba mengurai tanggal karena mengasumsikan format tanggal non-standar MS.
Dasar
16
Contoh kode cepat: var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();diikuti olehDictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Nate Cook
6
Keuntungan dari contoh Nate Cook dalam kasus sederhana adalah untuk menghindari perlunya DLL eksternal. Saya mengakses API dari konsol mandiri yang hanya dapat mengandalkan kerangka .Net.
Nick.T
@ pilavdzice Bisakah Anda menjelaskan lebih detail tentang itu? Saya tidak dapat mereproduksi "return null bukan string kosong", itu memberi saya nilai string kosong untukSomeData: ""
jrh
51

Bagi mereka yang mencari di internet dan menemukan posting ini, saya menulis posting blog tentang cara menggunakan kelas JavaScriptSerializer.

Baca selengkapnya ... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

Berikut ini sebuah contoh:

var json = "{\"id\":\"13\", \"value\": true}";
var jss = new JavaScriptSerializer();
var table = jss.Deserialize<dynamic>(json);
Console.WriteLine(table["id"]);
Console.WriteLine(table["value"]);
JP Richardson
sumber
hm, saya sudah mencoba solusi Anda ... Saya punya json seperti ini {"id": "13", "value": true} dan bagi saya hanya Kamus <dynamic> solusi yang berfungsi
Marko
ok saya menemukannya di mana masalahnya ... Anda perlu menambahkan [] setelah deklarasi kamus untuk deserialize dengan benar ... Saya juga menambahkan komentar ke posting blog Anda ... cheers;)
Marko
Saya telah memperbarui jawaban saya untuk mencerminkan dataset spesifik Anda. Ini berfungsi baik dengan dinamis.
JP Richardson
Saya baru saja menulis parser JSON lain yang sedikit lebih fleksibel dan mendukung Silverlight: procbits.com/2011/08/08/11 ...
JP Richardson
41

Mencoba untuk tidak menggunakan implementasi JSON eksternal jadi saya deserialised seperti ini:

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);
PvH
sumber
6
Tambahkan referensi System.Web.Extensions untuk menggunakan System.Web.Script
Patrick Cullen
1
Saya paling suka jawaban ini karena sederhana dan menggunakan .NET System.Web.Script.Serialization. Itu hanya bekerja. Saya bahkan dapat menggunakan JSON "tidak valid" string json = "{'id':13, 'value': true}";.
styfle
Karena penasaran, apakah ada cara satu baris yang sama untuk deserialize ke dalam kamus OrdinalIgnoreCase?
batbaatar
38

Saya memiliki masalah yang sama, jadi saya menulis ini sendiri. Solusi ini dibedakan dari jawaban lain karena dapat deserialize ke beberapa level.

Cukup kirim string JSON ke fungsi deserializeToDictionary itu akan mengembalikan objek yang tidak diketik dengan kuat Dictionary<string, object>.

Kode lama

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

Mis: Ini akan mengembalikan Dictionary<string, object>objek respons JSON Facebook.

Uji

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

Catatan: kampung halaman lebih lanjut disterilisasi menjadi Dictionary<string, object> objek.

Memperbarui

Jawaban lama saya berfungsi dengan baik jika tidak ada larik pada string JSON. Ini lebih lanjut deserialize ke List<object>jika elemen adalah array.

Cukup kirim string JSON ke fungsi deserializeToDictionaryOrList, ia akan mengembalikan objek yang tidak diketik dengan kuat Dictionary<string, object>atau 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;
    }
}
Dasun
sumber
@Jordan terima kasih telah menunjukkan, saya membuat beberapa modifikasi pada kode ini tetapi saya tidak memilikinya sekarang. Kode ini tidak menangani objek JArray, saya akan memperbarui kode begitu saya memilikinya.
Dasun
1
Bukan masalah. Saya hanya menyebutkannya karena mempelajari tentang isdan asoperator sangat membantu saya dan menyederhanakan kode saya sendiri.
Jordan
Ini berfungsi, tetapi tidak efisien, karena memanggil ToString dan kemudian Deserialize lagi. Lihatlah jawaban Falko di bawah ini. Ini menghilangkan kode string sumber hanya sekali.
Sergei Zinovyev
1
Jawaban Falko hanya berfungsi jika Anda mengetahui struktur data sebelumnya. Solusi ini dapat digunakan untuk string JSON.
Dasun
16

Jika Anda mencari jenis pendekatan yang ringan, tanpa tambahan referensi, mungkin sedikit kode yang baru saja saya tulis ini akan berfungsi (saya tidak bisa menjamin 100% kekokohan).

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

[Saya sadar ini melanggar OP Limitation # 1, tetapi secara teknis, Anda tidak menulisnya, saya melakukannya]

dexy
sumber
3
Itulah satu-satunya jawaban yang berfungsi untuk Silverlight dan tanpa ketergantungan! Silverlight tidak memiliki JavascriptSerializer atau Serializable. Dan tidak ada ketergantungan berarti tidak ada Json.NET, RestSharp atau MiniJSON. Hanya @DanCsharpster yang mencoba solusi lain yang mungkin, tetapi sayangnya itu tidak berfungsi untuk saya seperti yang ini.
Cœur
1
Apa yang salah dengan menambahkan referensi ke sesuatu yang sederhana seperti JSON.NET? Apakah harus ringan sehingga Anda tidak dapat referensi apa pun? Saya tidak mengatakan kode Anda tidak akan berfungsi, tetapi kapan pun Anda menggulirkan kode Anda sendiri, Anda jelas berisiko mengambil kode Anda tidak sekuat itu, untuk hal-hal seperti kasus tepi, atau secepat perpustakaan yang diuji seperti JSON.NET.
Dan Csharpster
1
Menggulirkan sendiri adalah ide yang buruk ketika Anda memiliki alternatif yang baik. Saya tahu tidak ada situasi yang harus begitu ringan. Dan saya lebih suka memiliki kode yang kurang optimal yang mudah dibaca dan diubah.
Jordan
3
Saya awalnya menulis sepotong kode karena saya tidak punya alternatif. Pertimbangkan hal-hal seperti Silverlight, atau penyedia berbagai jenis untuk produk Office, di mana menambahkan referensi eksternal ke proyek itu sangat bermasalah atau tidak mungkin.
dexy
Saya tahu ini beberapa tahun kemudian, tetapi ini masih merupakan pertanyaan yang sangat valid. Untuk siapa pun yang bertanya-tanya mengapa kita ingin menjadi begitu ringan, yah, jika Anda bekerja dengan SQL CLR C #, hanya ada begitu banyak pustaka "aman" yang dapat Anda gunakan dan System.RunTime.Serializationbukan salah satunya, sayangnya JSON.NET tergantung pada dan karenanya tidak bisa digunakan juga. Terima kasih dexy untuk pekerjaan Anda yang sangat baik, saya berani sedikit memperbaikinya untuk dapat membatalkan deserisasi array array, lihat kode yang diperbarui dalam jawaban saya di sini .
Alberto Rechy
15

Saya hanya perlu menguraikan kamus bersarang , seperti

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

di mana JsonConvert.DeserializeObjecttidak membantu. Saya menemukan pendekatan berikut:

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

The SelectTokenmemungkinkan Anda menggali ke bawah ke kolom yang diinginkan. Anda bahkan dapat menentukan jalur yang ingin "x.y.z"melangkah lebih jauh ke objek JSON.

Falko
sumber
JObject.Parse (json) .ToObject <Kamus <Guid, Daftar <int> >> () bekerja untuk saya dalam skenario saya, terima kasih
geedubb
11

System.Text.Json

Ini sekarang dapat dilakukan dengan menggunakan System.Text.Jsonbawaan untuk .net core 3.0. Sekarang dimungkinkan untuk men-deserialisasi JSON tanpa menggunakan perpustakaan pihak ketiga.

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

Juga tersedia di System.Text.Json paket nu-get jika menggunakan .Net Standard atau .Net Framework.

haldo
sumber
1
Iya! System.Text.Jsonadalah cara untuk pergi hari ini.
mfluehr
2
Ya, ini terlihat menjanjikan! Namun, perhatikan bahwa versi standar .NET Core 3.1 dari System.Text.Json tidak mendukung deserializing Kamus dengan kunci non-string. Sementara OP saya adalah tentang string, dalam prakteknya sekarang, saya memiliki banyak kunci Guid, jadi ini "menggigit" saya ketika mencoba untuk beralih. Itu juga tidak memiliki padanan dari beberapa atribut (diperlukan, dll.)
richardtallent
6

Mark Rendle memposting ini sebagai komentar , saya ingin mempostingnya sebagai jawaban karena itu satu-satunya solusi yang telah bekerja sejauh ini untuk mengembalikan keberhasilan dan kode kesalahan hasil json dari respons Google reCaptcha.

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

Sekali lagi terima kasih, Mark!

Bryan
sumber
1
JavaScriptSerializer sudah hampir usang. Dokumentasi mengatakan kita harus menggunakan JSON.NET ( docs.microsoft.com/en-us/dotnet/api/… )
Mario Lopez
Juga bagus untuk aplikasi bentuk web lawas di mana Anda tidak ingin menyertakan dependensi tambahan.
Andrew Grothe
5

Sunting: Ini berfungsi, tetapi jawaban yang diterima menggunakan Json.NET jauh lebih mudah. Meninggalkan yang satu ini seandainya seseorang membutuhkan kode BCL-only.

Itu tidak didukung oleh .NET framework of the box. Pengawasan yang mencolok - tidak semua orang perlu melakukan deserialisasi menjadi objek dengan properti bernama. Jadi saya akhirnya menggulung sendiri:

<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

Disebut dengan:

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

Maaf untuk campuran C # dan VB.NET ...

richardtallent
sumber
2
[TestMethod] public void TestSimpleObject () {const string json = @ "{" "Name" ":" "Bob" "," "Age" ": 42}"; var dict = new JavaScriptSerializer (). DeserializeObject (json) sebagai 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
Ini fantastis. Membantu dengan implementasi layanan WCF yang berinteraksi menggunakan JSON dengan klien berbasis browser.
Anton
@ Mark Rendle: Implementasi Anda sangat sederhana, dan HANYA yang telah bekerja untuk saya sejauh ini dalam mendapatkan keberhasilan dan hasil kesalahan-kode json. Saya sudah mencoba banyak solusi, jadi terima kasih telah memposting itu sebagai komentar. Seharusnya jawabannya.
Bryan
5

Saya telah menambahkan kode yang dikirimkan oleh jSnake04 dan Dasun di sini. Saya telah menambahkan kode untuk membuat daftar objek dari JArrayinstance. Ini memiliki rekursi dua arah tetapi karena berfungsi pada model pohon tetap, terbatas, tidak ada risiko stack overflow kecuali datanya masif.

/// <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;
}
Yordania
sumber
4

Saya menambahkan cek untuk nilai nol di JSON ke jawaban lainnya

Saya memiliki masalah yang sama jadi saya menulis ini sendiri. Solusi ini dibedakan dari jawaban lain karena dapat deserialize ke beberapa level.

Cukup kirim string json ke fungsi deserializeToDictionary itu akan mengembalikan objek yang tidak diketik dengan kuat 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;
}

Mis: Ini akan mengembalikan Dictionary<string, object>objek respons JSON 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);
}

Catatan: kota kelahiran lebih lanjut deserialize menjadi Dictionary<string, object>objek.

jSnake04
sumber
1
+1 Seperti yang saya katakan dengan Dasun di atas. Anda bisa memeriksa apakah d.Value is JObject. Anda tidak harus melalui refleksi untuk memeriksa jenis. Dan dengan isoperator Anda tidak perlu memeriksa nol. Mengembalikan nilai false jika objek adalah null.
Jordan
3

Tampaknya semua jawaban ini di sini hanya berasumsi bahwa Anda dapat memperoleh string kecil dari objek yang lebih besar ... bagi orang-orang yang ingin secara sederhana mendesentralisasi objek besar dengan kamus semacam itu di suatu tempat di dalam pemetaan, dan yang menggunakan System.Runtime.Serialization.Jsonsistem DataContract, inilah sebuah solusi:

Jawaban di gis.stackexchange.com memiliki tautan menarik ini . Saya harus memulihkannya dengan archive.org, tetapi ia menawarkan solusi yang cukup sempurna: IDataContractSurrogatekelas khusus di mana Anda menerapkan persis tipe Anda sendiri. Saya bisa mengembangkannya dengan mudah.

Saya membuat banyak perubahan di dalamnya. Karena sumber asli tidak lagi tersedia, saya akan memposting seluruh kelas di sini:

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

Untuk menambahkan tipe baru yang didukung ke kelas, Anda hanya perlu menambahkan kelas Anda, berikan konstruktor dan fungsi yang tepat (lihat SurrogateDictionarycontohnya), pastikan mewarisi JsonSurrogateObject, dan tambahkan pemetaan tipenya ke KnownTypeskamus. SurrogateDictionary yang disertakan dapat berfungsi sebagai dasar untuk semua Dictionary<String,T>jenis di mana T adalah jenis apa pun yang melakukan deserialisasi dengan benar.

Menyebutnya sangat sederhana:

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

Perhatikan bahwa karena alasan tertentu hal ini mengalami masalah menggunakan string kunci yang berisi spasi; mereka sama sekali tidak ada dalam daftar akhir. Mungkin saja itu hanya melawan spesifikasi json dan api yang saya panggil diimplementasikan dengan buruk, ingatlah; Saya tidak tahu. Bagaimanapun, saya memecahkan masalah ini dengan mengganti-ganti mereka dengan garis bawah pada data mentah json dan memperbaiki kamus setelah deserialisasi.

Nyergud
sumber
Ngomong-ngomong, untuk alasan tertentu, Mono tampaknya kesulitan menjalankan barang-barang ini ...
Nyerguds
Terima kasih telah berbagi, sayangnya solusi ini tidak mendukung tipe non-primitif, dan tidak ada cara untuk mendapatkan nilai mentah, sehingga Anda dapat membuatnya sendiri. Jika saya mendaftarkan jenis khusus saya di FamousTypes dan menggunakannya dalam kamus, ia memanggil kamus terlebih dahulu, saya akan mengharapkannya untuk memulai parsing dari bawah ke atas dari jenis yang paling jauh, ke yang lebih kompleks.
Ivan Leonenko
Nah, pertanyaannya hanya ditanyakan Dictionary<String,String>. Sejujurnya saya tidak pernah mencoba deserialisasi jenis kompleks dengan sistem ini.
Nyerguds
3

Berdasarkan komentar di atas cobaJsonConvert.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);

tampaknya berfungsi bahkan untuk objek dan daftar yang kompleks.

Tinjuan kemarahan
sumber
1

Saya baru saja mengimplementasikan ini di RestSharp . Posting ini sangat membantu saya.

Selain kode di tautan, ini adalah kode saya. Saya sekarang mendapatkan Dictionaryhasil ketika saya melakukan sesuatu seperti ini:

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

Perhatikan jenis JSON yang Anda harapkan - dalam kasus saya, saya mengambil satu objek dengan beberapa properti. Di tautan terlampir, penulis sedang mengambil daftar.

northben
sumber
1

Pendekatan saya secara langsung deserializes ke IDictionary, tanpa JObject atau ExpandObject di antaranya. Kode ini menggunakan konverter, yang pada dasarnya disalin dari kelas ExpandoObjectConverter yang ditemukan di kode sumber JSON.NET, tetapi menggunakan IDictionary alih-alih ExpandoObject.

Pemakaian:

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

Kode:

// 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;
    }
}
Rafał Kłys
sumber
1

Anda bisa menggunakan Tiny-JSON

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

Sedikit terlambat ke permainan, tetapi tidak ada solusi di atas menunjuk saya ke arah. NET murni dan sederhana, tidak ada solusi json.net. Jadi ini dia, akhirnya menjadi sangat sederhana. Di bawah ini adalah contoh lengkap bagaimana hal ini dilakukan dengan serialisasi .NET Json standar, contoh ini memiliki kamus baik dalam objek root dan objek anak.

Peluru emas adalah kucing ini, parsing pengaturan sebagai parameter kedua ke serializer:

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

Kode lengkap di bawah ini:

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);
                        }
                    }
                }
            }
        }
    }
Kjeld Poulsen
sumber
0

Cukup mengherankan, jika Anda ingin menggunakan pengikat model default, sepertinya Anda harus menggunakan nilai indeks numerik seperti formulir POST.

Lihat kutipan berikut dari artikel ini http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Meskipun agak berlawanan dengan intuisi, permintaan JSON memiliki persyaratan yang sama — permintaan itu juga harus mematuhi sintaks penamaan bentuk pos. Ambil, misalnya, muatan JSON untuk koleksi UnitPrice sebelumnya. Sintaks array JSON murni untuk data ini akan direpresentasikan sebagai:

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

Namun, penyedia nilai default dan pengikat model membutuhkan data untuk direpresentasikan sebagai form posting JSON:

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

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

Skenario pengumpulan objek yang kompleks mungkin merupakan salah satu skenario paling problematis yang dialami oleh pengembang karena sintaksis belum tentu jelas bagi semua pengembang. Namun, setelah Anda mempelajari sintaks yang relatif sederhana untuk memposting koleksi yang kompleks, skenario ini menjadi lebih mudah untuk ditangani.

jeremysawesome
sumber
0

Saya akan menyarankan menggunakan System.Runtime.Serialization.Jsonitu adalah bagian dari .NET 4.5.

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

Kemudian gunakan seperti ini:

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);
Dan Csharpster
sumber
Di mana serializer didefinisikan?
bnieland
..dan apa itu Category3MeasureModel? Tidak ada hit di Google.
bnieland
1
Itu hanya kelas model yang saya serialisasi untuk proyek saya. Seharusnya kelas Foo itu, tapi aku membuka kembali seluruh bagian dari kode produksi. Anda harus membuat sendiri, seperti kelas Foo saya. Saya menamainya menjadi Foo agar lebih sederhana. Ini hanya kelas properti atau bidang yang Anda ingin serialkan ke json dan kembali.
Dan Csharpster
1
@DanCsharpster Dengan copy-past yang pasti dari kode Anda, saya dapatkan, di Windows Phone 8.1 Silverlight: `Pengecualian tipe 'System.Security.SecurityException' terjadi di System.ServiceModel.Web.ni.dll tetapi tidak ditangani di pengguna kode Informasi tambahan: Jenis kontrak data 'MyApp.Foo' tidak dapat di-deserialisasi karena anggota 'Data' tidak umum. Membuat anggota menjadi publik akan memperbaiki kesalahan ini. Sebagai alternatif, Anda dapat membuatnya internal, dan menggunakan atribut InternalsVisibleToAttribute di majelis Anda untuk mengaktifkan serialisasi anggota internal
Cœur
1
@DanCsharpster Dan ketika mengubah data properti menjadi anggota (tanpa get; set;), saya mendapatkan: Pengecualian kesempatan pertama dari tipe 'System.ArgumentException' terjadi di System.ServiceModel.Web.ni.dll Informasi tambahan: Objek dari ketik 'System.Object' tidak dapat dikonversi untuk mengetikkan 'System.Collections.Generic.Dictionary`2 [System.String, System.String]'.
Cœur
0

Bagi siapa saja yang mencoba mengonversi JSON ke kamus hanya untuk mengambil beberapa nilai darinya. ada cara sederhana menggunakanNewtongsoft.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
wdanxna
sumber