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

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

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

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

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

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

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

Есть идеи?

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

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

Ответы [ 19 ]

4 голосов
/ 20 марта 2015

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

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

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

3 голосов
/ 20 декабря 2017

На основе комментариев выше попробовать 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);

, кажется, работает даже для сложных объектов и списков.

2 голосов
/ 06 августа 2015

Кажется, что все эти ответы здесь просто предполагают, что вы можете извлечь эту маленькую строку из более крупного объекта ... для людей, которые хотят просто десереализовать большой объект с таким словарем где-то внутри отображения, и которые используют 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 и исправив словарь после десериализации.

1 голос
/ 13 декабря 2017

Немного опоздал к игре, но ни одно из вышеперечисленных решений не указало мне путь к чистому и простому решению .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);
                        }
                    }
                }
            }
        }
    }
1 голос
/ 02 мая 2013

Я только что реализовал это в 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 голос
/ 26 мая 2014

Мой подход напрямую десериализуется в 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;
    }
}
0 голосов
/ 10 июня 2013

Я бы предложил использовать 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);
0 голосов
/ 21 сентября 2016

Вы можете использовать Tiny-JSON

string json = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
IDictionary<string, string> dict = Tiny.Json.Decode<Dictionary<string, string>>(json);
0 голосов
/ 30 мая 2012

Достаточно досадно, если вы хотите использовать связующие по умолчанию для моделей, похоже, вам придется использовать числовые значения индекса, такие как форма 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
}

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

...