Как я могу десериализовать JSON в простой словарь <string, string> в ASP.NET?

682

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

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

Я не пытаюсь десериализировать в сильно окрашенные объекты .NET

Мне просто нужен старый старый словарь (Of String, String) или какой-то эквивалент (хеш-таблица, Dictionary (Of String, Object), StringDictionary старой школы - черт, мне пригодится двухмерный массив строк.

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

По-видимому, ни одна из этих библиотек JSON не обладает такой очевидной способностью убивать лоб - они полностью сосредоточены на десериализации, основанной на отражениях, через строгие контракты.

Любые идеи?

Ограничения:

  1. Я не хочу реализовывать свой собственный анализатор JSON
  2. Еще не могу использовать ASP.NET 4.0
  3. Предпочел бы держаться подальше от устаревшего устаревшего класса ASP.NET для JSON
richardtallent
источник
1
Re: ограничение 3, JavaScriptSerizlizerиспользуется в ASP.NET MVC и больше не рекомендуется.
bdukes
17
Невероятно, как трудно было найти простой способ преобразовать строку json во что-то, что я мог бы легко использовать, не пролистывая множество различных стековых потоков. В других языках это так просто, но Java и C # делают все возможное, чтобы усложнить жизнь.
user299709

Ответы:

894

Json.NET делает это ...

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

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

Дополнительные примеры: сериализация коллекций с помощью Json.NET

Джеймс Ньютон-Кинг
источник
9
Это также работает, когда ваши значения являются целыми числами. Они автоматически приводятся к «строкам»?
Highmastdon
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.JavaScriptSerializerв System.Web.Extensionsсборке 3.5 . Используйте метод DeserializeObject(String).

Я наткнулся на это, когда делал пост ajax (через jquery) типа контента 'application / json' для статического .net Page Method и увидел, что метод (у которого был один параметр типа 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);
Nate Кук
6
Преимущество примера Нейта Кука в простом случае состоит в том, чтобы избежать необходимости во внешних DLL. Я получаю доступ к API из автономной консоли, которая может полагаться только на .Net Framework.
Nick.T
@pilavdzice Можете ли вы рассказать об этом подробнее? Я не могу воспроизвести объект «вернуть пустое значение вместо пустых строк», он дал мне пустое строковое значение для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"]);
JP Richardson
источник
хм, я попробовал ваше решение ... У меня есть json как {{id ': "13", "value": true} и только для меня работает решение Dictionary <dynamic>
Marko
хорошо, я обнаружил, в чем проблема ... вам нужно добавить [] после объявления словаря для правильной десериализации ... Я тоже добавляю комментарий к вашему сообщению в блоге ... ура;)
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}";.
Styfle
Из любопытства, есть ли такой же однострочный способ десериализации в словарь OrdinalIgnoreCase?
батбаатар
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>объект ответа 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);
}

Примечание: родной город далее опустошается в 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;
    }
}
Dasun
источник
@ Джордан, спасибо за указание, я сделал некоторые изменения в этом коде, но у меня его сейчас нет. Этот код не обрабатывает объекты JArray, я обновлю код, как только он у меня будет.
Дасун
1
Не проблема. Я упоминаю об этом только потому, что изучение операторов isand asочень помогло мне и упростило мой собственный код.
Иордания
Это работает, но не эффективно, потому что вызывает ToString, а затем снова десериализуется. Посмотрите на ответ Фалько ниже. Он десериализует исходную строку только один раз.
Сергей Зиновьев
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, но технически вы не написали это, я сделал]

Dexy
источник
3
Это единственный ответ, работающий на Silverlight и не зависящий! Silverlight не имеет JavascriptSerializer или Сериализуемый. А отсутствие зависимости означает отсутствие Json.NET, RestSharp или MiniJSON. Только @DanCsharpster попробовал другое возможное решение, но, к сожалению, оно не сработало для меня, как это делает.
Cœur
1
Что плохого в добавлении ссылки на что-то простое, например, JSON.NET? Должен ли он быть таким легким, чтобы на него нельзя было ссылаться? Я не говорю, что ваш код не будет работать, но всякий раз, когда вы создаете свой собственный код, вы, очевидно, рискуете, что ваш код будет не настолько надежным, например, для крайних случаев, или быстрым, как проверенная библиотека, такая как JSON.NET.
Дэн Чарпстер
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 <Dictionary <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.

Haldo
источник
1
Да! System.Text.Jsonэто путь в эти дни.
mfluehr
2
Да, это выглядит многообещающе! Однако обратите внимание, что версия System.Text.Json по умолчанию для .NET Core 3.1 не поддерживает десериализацию словарей с нестроковыми ключами. В то время как мой OP был посвящен строкам, на практике у меня много клавиш Guid, так что это «кусает» меня, когда я пытаюсь переключиться. Он также не имеет эквивалентов некоторых атрибутов (обязательных и т. Д.).
Ричардталент
6

Марк Рендл опубликовал это как комментарий , я хотел опубликовать его как ответ, так как это единственное решение, которое до сих пор работало, чтобы вернуть успех, и коды ошибок json являются результатом ответа Google reCaptcha.

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

Еще раз спасибо, Марк!

Bryan
источник
1
JavaScriptSerializer почти устарел. В документации сказано, что мы должны использовать JSON.NET ( docs.microsoft.com/en-us/dotnet/api/… )
Марио Лопес,
Также подходит для устаревших приложений веб-форм, в которые вы не хотите добавлять дополнительные зависимости.
Эндрю Грот
5

Изменить: это работает, но принятый ответ с использованием Json.NET гораздо проще. Оставьте это на тот случай, если кому-то нужен код только для BCL.

Он не поддерживается платформой .NET из коробки. Яркий недосмотр - не всем нужно десериализовать объекты с именованными свойствами. Итак, я закончил кататься самостоятельно:

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

richardtallent
источник
2
[TestMethod] public void TestSimpleObject () {const string json = @ "{" "Name" ":" "Bob" "," "Age" ": 42}"; var dict = new JavaScriptSerializer (). DeserializeObject (json) as IDictionary <строка, объект>; Assert.IsNotNull (ДИКТ); Assert.IsTrue (dict.ContainsKey ( "Name")); Assert.AreEqual ("Bob", dict ["Name"]); Assert.IsTrue (dict.ContainsKey ( "Возраст")); Assert.AreEqual (42, dict ["Age"]); }
Марк Рендл
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>объект ответа 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);
}

Примечание: родной город далее десериализуется в Dictionary<string, object>объект.

jSnake04
источник
1
+1 Как я уже говорил с Dasun выше. Вы можете просто проверить, есть ли d.Value is JObject. Вам не нужно проходить рефлексию для проверки типов. А с isоператором вам не нужно проверять на ноль. Возвращает false, если объект нулевой.
Джордан
3

Кажется, что все эти ответы здесь просто предполагают, что вы можете получить эту маленькую строку из большего объекта ... для людей, которые хотят просто десереализовать большой объект с таким словарем где-то внутри отображения, и кто использует System.Runtime.Serialization.Json DataContract, вот решение:

Ответ на 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
источник
Кстати, по какой-то особой причине Моно, похоже, испытывает проблемы с управлением этим материалом ...
Nyerguds
Спасибо, что поделились, к сожалению, это решение не поддерживает непримитивные типы, и нет никакого способа получить необработанное значение, так что вы можете создать его самостоятельно. Если я зарегистрирую свой пользовательский тип в KnownTypes и использую его в словаре, он сначала вызывает словарь, я ожидаю, что он начнет анализировать снизу вверх от самых удаленных типов к более сложным.
Иван Леоненко
Ну, вопрос только о Dictionary<String,String>. Честно говоря, я никогда не пытался десериализовать сложные типы с помощью этой системы.
Нергудс
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 вы ожидаете - в моем случае я извлекал один объект с несколькими свойствами. В прикрепленной ссылке автор извлекал список.

northben
источник
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
}

Сценарий сбора сложных объектов является, пожалуй, одним из наиболее проблемных сценариев, с которыми сталкиваются разработчики, поскольку синтаксис не обязательно очевиден для всех разработчиков. Однако, как только вы изучите относительно простой синтаксис для публикации сложных коллекций, с этими сценариями станет намного легче иметь дело.

jeremysawesome
источник
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.
bnieland
1
Это просто модельный класс, который я сериализую для своего проекта. Предполагается, что это класс Foo, но я скопировал весь раздел из производственного кода. Вы должны создать свой собственный, как мой класс Foo. Я переименовал его в Foo, чтобы сделать его проще. Это просто класс свойств или полей, которые вы хотите сериализовать в json и обратно.
Дэн Чарпстер
1
@DanCsharpster С точной копией вашего кода я получаю на Windows Phone 8.1 Silverlight: `Исключение типа 'System.Security.SecurityException' произошло в System.ServiceModel.Web.ni.dll, но не было обработано пользователем код Дополнительная информация: Тип контракта данных «MyApp.Foo» не может быть десериализован, поскольку член «Данные» не является открытым. Обнародование этого члена исправит эту ошибку. Кроме того, вы можете сделать его внутренним и использовать атрибут InternalsVisibleToAttribute в своей сборке, чтобы включить сериализацию внутренних элементов
Cœur
1
@DanCsharpster И при изменении свойства Data, чтобы стать членом (без 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
wdanxna
источник