Як я можу десериалізувати JSON до простого словника <string, string> в ASP.NET?


682

У мене простий список ключів / значень у JSON надсилається назад на ASP.NET через POST. Приклад:

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

Я НЕ ВІДПОВІДЖУЄМО ДЕЗЕРІАЛІЗУВАТИ ДІЯЛЬНІ ОБ'ЄКТИ

Мені просто потрібен звичайний старий словник (Of String, String) або якийсь еквівалент (хеш-таблиця, Словник (String, Object), Old-School StringDictionary - пекло, для мене буде працювати 2-D масив рядків).

Я можу використовувати все, що доступне в ASP.NET 3.5, а також популярний Json.NET (який я вже використовую для серіалізації для клієнта).

Мабуть, жодна з цих бібліотек JSON не має такої очевидної можливості відхилення від лоба - вони повністю зосереджені на дезіріалізації на основі роздумів за допомогою сильних контрактів.

Будь-які ідеї?

Обмеження:

  1. Я не хочу реалізувати власний аналізатор JSON
  2. Поки не можна використовувати ASP.NET 4.0
  3. Я вважаю за краще триматися подалі від старого, застарілого класу ASP.NET для JSON

1
re: обмеження 3, JavaScriptSerizlizerвикористовується в ASP.NET MVC і більше не застаріло.
бдукс

17
неймовірно, як важко було знайти простий спосіб перетворити рядок json у те, що я міг би легко використати, не прогортаючи багато різних stackoverflow. На інших мовах так просто, але Java та C #, схоже, виходять зі шляху, щоб ускладнити життя.
user299709

Відповіді:


893

Json.NET робить це ...

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

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

Ще приклади: серіалізація колекцій за допомогою Json.NET


9
Чи це також працює, коли ваші значення є цілими числами. Чи автоматично вони передаються на "рядки"?
Хаймастдон

58
@Highmastdon Ні, це не так. Я знайшов найкращий спосіб десеріалізації в словнику - використовувати dynamicяк тип для значень:JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
Ерік Щербоом

1
Спробував кілька відповідей на цій сторінці з дуже брудною парою ключ / значення, і JSON.NET був єдиним, що я спробував, що спрацювало.
bnieland

15
Не працює, якщо ви використовуєте масив пар ключових значень у json, [{key: "a", value: "1"}, {key: "b", value:"2"}]ви повинні зробити щось подібне:var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
Адріан

8
Також не працює, якщо значення є вкладеними об'єктами, оскільки json.net створює такі, як JObjects
Kugel,

100

Я виявив .NET має вбудований спосіб передати рядок JSON в тип Dictionary<String, Object>via через System.Web.Script.Serialization.JavaScriptSerializer3.5 System.Web.Extensions. Використовуйте метод DeserializeObject(String).

Я наткнувся на це, виконуючи публікацію ajax (через jquery) типу вмісту 'application / json' для статичного методу .net Page Page, і побачив, що метод (який мав єдиний параметр типу Object) магічно отримав цей словник.


5
але вбудований javascriptserializer грішніший, ніж json.net, це рішення краще. Наприклад, javascriptseralizer поверне нулі замість пустих рядків і взагалі не працює для змінних властивостей тощо.
pilavdzice

1
@pilavdzice Не кажучи вже про задоволення, яке ви маєте при спробі розбору дат, оскільки він передбачає нестандартний формат дати MS.
Основний

16
Короткий приклад коду: var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();даліDictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Нейт Кук

6
Перевагою прикладу Нейт-Кука в простому випадку є уникнення необхідності у зовнішніх DLL-файлах. Я звертаюся до API через окрему консоль, на яку можна покластися лише .Net Framework.
Nick.T

@pilavdzice Чи можете ви детальніше про це піти? Я не можу відтворити річ "return null замість пустих рядків", це дало мені значення пустого рядка дляSomeData: ""
jrh

51

Для тих, хто шукає Інтернет та натрапляє на цю публікацію, я написав повідомлення в блозі про те, як використовувати клас JavaScriptSerializer.

Детальніше ... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

Ось приклад:

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

хм, я спробував ваше рішення ... у мене є такий json {"id": "13", "value": true} і для мене працює лише словник <динамічний> рішення
Марко

ОК, я знайшов це, де проблема ... вам потрібно додати [] після декларування словника, щоб дезаріалізувати належним чином ... Я додаю коментар до вашого блогу теж ... ура;)
Marko

Я оновив свою відповідь, щоб відобразити ваш конкретний набір даних. Це добре працює з динамічним.
JP Richardson

Я щойно написав ще один аналізатор JSON, який є трохи більш гнучким і підтримує Silverlight: procbits.com/2011/08/11/…
JP Richardson

41

Намагався не використовувати жодної зовнішньої реалізації JSON, тому я десеріалізувався так:

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
Додайте посилання System.Web.Extensions, щоб використовувати System.Web.Script
Патрік Каллен

1
Мені найбільше подобається ця відповідь, оскільки вона проста і використовує .NET System.Web.Script.Serialization. Це просто працює. Мені навіть вдалося скористатися "недійсним" JSON, як string json = "{'id':13, 'value': true}";.
стайф

З цікавості, чи є той самий один рядковий спосіб деріаріалізації в словнику OrdinalIgnoreCase?
batbaatar

38

У мене була така ж проблема, тож я написав це сам. Це рішення відрізняється від інших відповідей, оскільки може дезаріалізуватися на декількох рівнях.

Просто надішліть рядок JSON в функцію deserializeToDictionary, вона поверне не сильно набраний 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.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;
}

Наприклад: Це поверне Dictionary<string, object>об'єкт відповіді Facebook JSON.

Тест

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

Примітка: рідне місто ще більше десерилізується в Dictionary<string, object> об’єкт.

Оновлення

Моя стара відповідь чудово працює, якщо в рядку JSON немає масиву. Це ще більше деріаріалізувати в, List<object>якщо елемент - це масив.

Просто надішліть рядок JSON в функцію deserializeToDictionaryOrList, вона поверне не сильно набраний Dictionary<string, object>об'єкт або 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 спасибі за вказівку, я вніс деякі зміни в цей код, але у мене його зараз немає. Цей код не обробляє об'єкти JArray, я оновлюю його, як тільки його матиму.
Дасун

1
Не проблема. Я тільки згадати, тому що навчання про isта asоператорах дуже допомогла мені і спростив свій власний код.
Йордан

Це працює, але не ефективно, бо викликає ToString, а потім знову Deserialize. Подивіться відповідь Фалько нижче. Це дезаріалізувати рядок джерела лише один раз.
Сергій Зіновієв

1
Відповідь Фалько працює, лише якщо ви заздалегідь знаєте структуру даних. Це рішення можна використовувати для будь-якого рядка JSON.
Дасун

16

Якщо ви дотримуєтесь легкого підходу без додавання посилань, можливо, цей біт коду, який я щойно написав, спрацює (хоча я не можу на 100% гарантувати надійність).

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

[Я розумію, що це порушує обмеження №1 ОП, але технічно ви цього не написали, я це зробив]


3
Це єдина відповідь, яка працює для Silverlight та без залежності! У Silverlight немає JavascriptSerializer або Serializable. І жодна залежність не означає Json.NET, RestSharp або MiniJSON. Тільки @DanCsharpster спробував інше можливе рішення, але, на жаль, він не працював для мене, як це робить.
Cœur

1
Що не так з додаванням посилання на щось таке, як JSON.NET? Чи потрібно, щоб він був таким легким, що ви нічого не можете згадати? Я не кажу, що ваш код не працюватиме, але щоразу, коли ви робите свій власний, ви, очевидно, ризикуєте, що ваш код не буде настільки надійним для таких речей, як крайові корпуси, або швидкий, як перевірена бібліотека, наприклад JSON.NET.
Dan Csharpster

1
Прокатка свого - погана ідея, коли у вас є хороша альтернатива. Я не знаю жодної ситуації, яка повинна бути такою легкою. І я вважаю за краще менш оптимальний код, який легко читати та змінювати.
Йордан

3
Я спочатку написав цей фрагмент коду, оскільки не мав альтернативи. Розгляньте такі речі, як Silverlight або постачальників різних типів для продуктів Office, де додавання зовнішніх посилань на проект є вкрай проблематичним або неможливим.
dexy

Я знаю його через кілька років, але це все ще дуже вагоме питання. Комусь цікаво, чому ми хотіли б так легко отримати, ну якщо ви працюєте з SQL CLR C #, є лише стільки "безпечних" бібліотек, якими ви можете користуватися, і System.RunTime.Serializationне одна з них, на жаль, залежить від JSON.NET це не може бути використане. Дякую dexy за вашу чудову роботу, я наважився трохи покращити його, щоб мати можливість дезаріалізувати масиви масивів, дивіться оновлений код у моїй відповіді тут .
Альберто Речі

15

Мені просто потрібно було розібрати вкладений словник, наприклад

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

де JsonConvert.DeserializeObjectне допомагає. Я знайшов такий підхід:

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

SelectTokenДозволяє докопатися до потрібного поля. Ви навіть можете вказати шлях, схожий "x.y.z"на крок далі в об’єкт JSON.


JObject.Parse (json) .ToObject <Словник <Guid, List <int> >> () працював на мене за моїм сценарієм дякую
geedubb

11

System.Text.Json

Тепер це можна зробити за допомогою System.Text.Jsonвбудованого в нього .net core 3.0. Тепер можна деріаріалізувати JSON без використання сторонніх бібліотек.

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

Також доступний у пакеті nu-get System.Text.Json, якщо використовується .Net Standard або .Net Framework.


1
Так! System.Text.Jsonце шлях, який потрібно пройти в ці дні.
mfluehr

2
Так, це виглядає перспективно! Однак зауважте, що .NET Core 3.1 за замовчуванням версія System.Text.Json не підтримує десеріалізацію словників за допомогою рядкових ключів. Поки моя ОП стосувалася рядків, зараз на практиці у мене є багато клавіш Guid, тому це "вкусило" мене при спробі зробити перемикач. Він також не має еквівалентів деяких атрибутів (обов'язкових тощо).
richardtallent

6

Марк Рендл розмістив це як коментар , я хотів опублікувати це як відповідь, оскільки це єдине рішення, яке до цього часу працювало, щоб повернути успіх і json-коди помилок, отримані у відповіді Google reCaptcha.

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

Ще раз дякую, Марку!


1
JavaScriptSerializer майже застарів. У документації сказано, що ми повинні використовувати JSON.NET ( docs.microsoft.com/en-us/dotnet/api/… )
Маріо Лопес

Також добре підходить для застарілих додатків веб-форм, де ви не хочете включати додаткові залежності.
Ендрю Гроте

5

Редагувати: Це працює, але прийнята відповідь за допомогою Json.NET набагато простіша. Залиште цей, якщо комусь потрібен лише код BCL.

Він не підтримується .NET Framework поза коробкою. Надзвичайний нагляд - не всім потрібно дезаріалізувати об'єкти з названими властивостями. Тож я закінчила прокат свого:

<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

Телефонується з:

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

Вибачте за суміш C # та VB.NET…


2
[TestMethod] public void TestSimpleObject () {const string json = @ "{" "Ім'я" ":" Bob "", "Age" ": 42}"; var dict = новий JavaScriptSerializer (). DeserializeObject (json) як IDictionary <рядок, об'єкт>; Assert.IsNotNull (dict); Assert.IsTrue (dict.ContainsKey ("Ім'я")); Assert.AreEqual ("Bob", dict ["Ім'я"]); Assert.IsTrue (dict.ContainsKey ("Вік")); Assert.AreEqual (42, dict ["Вік"]); }
Відмітити Rendle

1
Це фантастично. Допомагає реалізаціям служб WCF, які інтерфейсують за допомогою JSON з клієнтами на основі браузера.
Антон

@Mark Rendle: Ваша реалізація настільки проста, і це ТІЛЬКИ, який працював для мене досі в отриманні результатів і кодів помилок json. Я спробував багато рішень, тож дякую, що ви написали це як коментар. Це має бути відповіддю.
Брайан

5

Я додав код, поданий jSnake04 та Dasun. Я додав код для створення списків об'єктів з JArrayекземплярів. У нього є двостороння рекурсія, але оскільки вона функціонує за фіксованою, кінцевою моделлю дерева, немає ризику переповнення стека, якщо дані не є масивними.

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

До іншої відповіді я додав чек на нульові значення в JSON

У мене була така ж проблема, тому я написав це сам. Це рішення відрізняється від інших відповідей, оскільки може дезаріалізуватися на декількох рівнях.

Просто надішліть рядок json в функцію deserializeToDictionary, вона поверне не сильно набраний 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;
}

Наприклад: Це поверне Dictionary<string, object>об'єкт відповіді Facebook JSON.

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

Примітка: рідне місто ще більше деріаріалізується в Dictionary<string, object>об’єкт.


1
+1 Як я вже говорив з Дасуном вище. Ви могли просто перевірити, чи є d.Value is JObject. Щоб перевірити типи, вам не потрібно пройти рефлексію. А з isоператором вам не потрібно перевіряти на null. Він повертає значення false, якщо об'єкт недійсний.
Йордан

3

Здається, що всі ці відповіді тут просто припускають, що ви можете отримати цю маленьку рядок з більшого об'єкта ... для людей, які хочуть просто деареалізувати великий об’єкт із таким словником десь усередині карти, і хто використовує систему System.Runtime.Serialization.JsonDataContract, ось ось вирішення:

Відповідь на gis.stackexchange.com мала це цікаве посилання . Мені довелося відновити його з archive.org, але він пропонує досить ідеальне рішення: замовнийIDataContractSurrogate клас, в якому ви реалізуєте саме свої типи. Мені це вдалося легко розширити.

Я вніс купу змін. Оскільки оригінального джерела більше немає, я опублікую весь клас тут:

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

Щоб додати до класу нові підтримувані типи, вам просто потрібно додати свій клас, надати йому потрібні конструктори та функції (подивіться на SurrogateDictionaryприклад), переконайтесь, що він успадковується JsonSurrogateObject, і додайте відображення його типу до KnownTypesсловника. Включений SurrogateDictionary може послужити основою для будь-якогоDictionary<String,T> типів, де T - будь-який тип, який десеріалізується правильно.

Викликати це дуже просто:

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

Зауважте, що чомусь ця річ має проблеми з використанням ключових рядків, які містять пробіли; їх просто не було в остаточному списку. Можливо, це просто проти специфікацій json, і api, який я закликав, було погано реалізованим, зауважте; Я не знаю. У всякому разі, я вирішив це, замінивши їх регулярними виразами підкресленнями в необроблених даних json і виправивши словник після деріаріалізації.


До речі, чомусь Моно, здається, має проблеми з керуванням цим матеріалом ...
Nyerguds

Дякуємо за спільний доступ, на жаль, це рішення не підтримує непримітивні типи, і неможливо отримати вихідну цінність, тож ви зможете сконструювати його самостійно. Якщо я зареєструю свій власний тип у KknownTypes і використовую його у словнику, він спочатку викликає словник, я б очікував, що він почне аналізувати знизу вгору від найвіддаленіших типів, до більш складних.
Іван Леоненко

Ну, питання лише про це Dictionary<String,String>. Я, чесно кажучи, ніколи не пробував десеріалізувати складні типи за допомогою цієї системи.
Nyerguds

3

На основі коментарів, спробуйте вищеJsonConvert.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);

здається, працює навіть для складних об'єктів та списків.


1

Я щойно реалізував це в RestSharp . Це повідомлення була мені корисною.

Окрім коду за посиланням, ось мій код. Тепер я отримую Dictionaryрезультати, коли роблю щось подібне:

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

Пам’ятайте про тип JSON, якого ви очікуєте - в моєму випадку я шукав один об'єкт з кількома властивостями. У доданому посиланні автор витягував список.


1

Мій підхід прямо десеріалізується до IDictionary, без JObject або ExpandObject між ними. Код використовує конвертер, який в основному копіюється з класу ExpandoObjectConverter, знайденого у вихідному коді JSON.NET, але використовуючи IDictionary замість ExpandoObject.

Використання:

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

Код:

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

Ви можете використовувати Tiny-JSON

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

1

Трохи запізнювався в грі, але не з перерахованих вище рішень вказав на мене в напрямку чистого і простого .NET, без рішення json.net. Так ось воно, в кінцевому підсумку, дуже просто. Нижче наведено повний приклад того, як це робиться зі стандартною серіалізацією .NET Json, приклад має словник як в кореневому об'єкті, так і в дочірніх об'єктах.

Золота куля - це ця кішка, проаналізуйте налаштування як другий параметр серіалізатора:

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

Повний код нижче:

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

Досить прикро, якщо ви хочете використовувати типові палітурки моделі за замовчуванням, схоже, вам доведеться використовувати числові значення індексу, як форму POST.

Дивіться наступний уривок із цієї статті http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Хоча це і є дещо протизаконним, запити JSON мають однакові вимоги - вони теж повинні дотримуватися синтаксису форми після іменування форми. Візьмемо, наприклад, корисний вантаж JSON для попередньої колекції UnitPrice. Чистий синтаксис масиву JSON для цих даних буде представлений у вигляді:

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

Однак постачальники значень за замовчуванням і палітурки моделей вимагають, щоб дані були представлені як повідомлення форми JSON:

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

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

Складний сценарій збору об'єктів - це, мабуть, один із найбільш проблемних сценаріїв, з якими стикаються розробники, оскільки синтаксис не обов'язково очевидний для всіх розробників. Однак, коли ви дізнаєтесь відносно простий синтаксис для розміщення складних колекцій, з цими сценаріями стає набагато простіше впоратися.


0

Я б запропонував використовувати System.Runtime.Serialization.Jsonце частину .NET 4.5.

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

Потім використовуйте його так:

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

Де визначено серіалізатор?
bnieland

..і що таке Category3MeasureModel? Немає звернень у Google.
Бніленд

1
Ось тільки клас моделей, який я серіалізую для свого проекту. Це повинен бути той клас Foo, але я скопіював весь розділ із виробничого коду. Ви повинні створити свій власний, як у мене клас Foo. Я перейменував його на Foo, щоб зробити його більш простим. Це просто клас властивостей або полів, які ви хочете серіалізувати в json і назад.
Dan Csharpster

1
@DanCsharpster З точним минулим копіюванням вашого коду я потрапляю на Windows Phone 8.1 Silverlight: `Виняток типу 'System.Security.SecurityException' стався в System.ServiceModel.Web.ni.dll, але користувач не оброблявся код Додаткова інформація: Тип договору даних "MyApp.Foo" не може бути дезаріалізований, оскільки член "Дані" не є загальнодоступним. Оголошення учасника публічно виправить цю помилку. Крім того, ви можете зробити його внутрішнім і використовувати атрибут InternalsVisibleToAttribute для своєї збірки, щоб увімкнути серіалізацію внутрішніх членів
Cœur

1
@DanCsharpster І змінюючи Дані властивості, щоб бути членом (без get; set;), я отримую: Перший винятковий виняток типу "System.ArgumentException" стався в System.ServiceModel.Web.ni.dll Додаткова інформація: Об'єкт тип 'System.Object' неможливо перетворити у тип 'System.Collections.Generic.Dictionary`2 [System.String, System.String]'.
Cœur

0

Для тих, хто намагається перетворити JSON у словник лише для отримання певного значення з нього. існує простий спосіб використанняNewtongsoft.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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.