Как использовать JSON.NET для десериализации в вложенный / рекурсивный словарь и список?

Мне нужно десериализовать сложную блочную JSON в стандартные .NET-контейнеры для использования в коде, который не знает JSON . Он ожидает, что все будет в стандартных типах .NET, в частности Dictionary [string, object] или List [object], где «объект» может быть примитивным или recurse (словарь или список).

Я не могу использовать статический тип для сопоставления результатов, а JObject / JToken не подходит. В идеале, может быть каким-то образом (через Контракты, возможно?), Чтобы преобразовать raw JSON в основные .NET-контейнеры.

Я искал все, чтобы уговорить десериализатор JSON.NET на создание этих простых типов, когда он сталкивается с «{}» или «[]», но с небольшим успехом.

Любая помощь ценится!

Если вы просто хотите использовать общий метод, который может обрабатывать любой произвольный JSON и преобразовывать его во вложенную структуру обычных типов .NET (примитивы, списки и словари), вы можете использовать API LINQ to JSON API JSON.Net для этого:

using System.Linq; using Newtonsoft.Json.Linq; public static class JsonHelper { public static object Deserialize(string json) { return ToObject(JToken.Parse(json)); } private static object ToObject(JToken token) { switch (token.Type) { case JTokenType.Object: return token.Children() .ToDictionary(prop => prop.Name, prop => ToObject(prop.Value)); case JTokenType.Array: return token.Select(ToObject).ToList(); default: return ((JValue)token).Value; } } } 

Вы можете вызвать метод, как показано ниже. obj будет либо содержать Dictionary , List , либо примитив в зависимости от того, с каким JSON вы начали.

 object obj = JsonHelper.Deserialize(jsonString); 

Один из способов десериализации строки json рекурсивно в словари и списки с JSON.NET – это создание пользовательского json-конвертерного classа, который происходит из абстрактного classа JsonConverter, предоставляемого JSON.NET.

Именно в вашем производном JsonConverter вы помещаете реализацию того, как объект должен быть записан в json и из него.

Вы можете использовать свой JsonConverter следующим образом:

 var o = JsonConvert.DeserializeObject>(json, new DictionaryConverter()); 

Вот пользовательский JsonConverter, который я использовал с успехом в прошлом, чтобы достичь тех же целей, что и в вашем вопросе:

 public class DictionaryConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { this.WriteValue(writer, value); } private void WriteValue(JsonWriter writer, object value) { var t = JToken.FromObject(value); switch (t.Type) { case JTokenType.Object: this.WriteObject(writer, value); break; case JTokenType.Array: this.WriteArray(writer, value); break; default: writer.WriteValue(value); break; } } private void WriteObject(JsonWriter writer, object value) { writer.WriteStartObject(); var obj = value as IDictionary; foreach (var kvp in obj) { writer.WritePropertyName(kvp.Key); this.WriteValue(writer, kvp.Value); } writer.WriteEndObject(); } private void WriteArray(JsonWriter writer, object value) { writer.WriteStartArray(); var array = value as IEnumerable; foreach (var o in array) { this.WriteValue(writer, o); } writer.WriteEndArray(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return ReadValue(reader); } private object ReadValue(JsonReader reader) { while (reader.TokenType == JsonToken.Comment) { if (!reader.Read()) throw new JsonSerializationException("Unexpected Token when converting IDictionary"); } switch (reader.TokenType) { case JsonToken.StartObject: return ReadObject(reader); case JsonToken.StartArray: return this.ReadArray(reader); 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 reader.Value; default: throw new JsonSerializationException (string.Format("Unexpected token when converting IDictionary: {0}", reader.TokenType)); } } private object ReadArray(JsonReader reader) { IList list = new List(); while (reader.Read()) { switch (reader.TokenType) { case JsonToken.Comment: break; default: var v = ReadValue(reader); list.Add(v); break; case JsonToken.EndArray: return list; } } throw new JsonSerializationException("Unexpected end when reading IDictionary"); } private object ReadObject(JsonReader reader) { var obj = new Dictionary(); while (reader.Read()) { switch (reader.TokenType) { case JsonToken.PropertyName: var propertyName = reader.Value.ToString(); if (!reader.Read()) { throw new JsonSerializationException("Unexpected end when reading IDictionary"); } var v = ReadValue(reader); obj[propertyName] = v; break; case JsonToken.Comment: break; case JsonToken.EndObject: return obj; } } throw new JsonSerializationException("Unexpected end when reading IDictionary"); } public override bool CanConvert(Type objectType) { return typeof(IDictionary).IsAssignableFrom(objectType); } } 

Вот эквивалент в f# :

 type IDictionaryConverter() = inherit JsonConverter() let rec writeValue (writer: JsonWriter) (value: obj) = let t = JToken.FromObject(value) match t.Type with | JTokenType.Object -> writeObject writer value | JTokenType.Array -> writeArray writer value | _ -> writer.WriteValue value and writeObject (writer: JsonWriter) (value: obj) = writer.WriteStartObject () let obj = value :?> IDictionary for kvp in obj do writer.WritePropertyName kvp.Key writeValue writer kvp.Value writer.WriteEndObject () and writeArray (writer: JsonWriter) (value: obj) = writer.WriteStartArray () let array = value :?> IEnumerable for o in array do writeValue writer o writer.WriteEndArray () let rec readValue (reader: JsonReader) = while reader.TokenType = JsonToken.Comment do if reader.Read () |> not then raise (JsonSerializationException("Unexpected token when reading object")) match reader.TokenType with | JsonToken.Integer | JsonToken.Float | JsonToken.String | JsonToken.Boolean | JsonToken.Undefined | JsonToken.Null | JsonToken.Date | JsonToken.Bytes -> reader.Value | JsonToken.StartObject -> readObject reader Map.empty | JsonToken.StartArray -> readArray reader [] | _ -> raise (JsonSerializationException(sprintf "Unexpected token when reading object: %O" reader.TokenType)) and readObject (reader: JsonReader) (obj: Map) = match reader.Read() with | false -> raise (JsonSerializationException("Unexpected end when reading object")) | _ -> reader.TokenType |> function | JsonToken.Comment -> readObject reader obj | JsonToken.PropertyName -> let propertyName = reader.Value.ToString () if reader.Read() |> not then raise (JsonSerializationException("Unexpected end when reading object")) let value = readValue reader readObject reader (obj.Add(propertyName, value)) | JsonToken.EndObject -> box obj | _ -> raise (JsonSerializationException(sprintf "Unexpected token when reading object: %O" reader.TokenType)) and readArray (reader: JsonReader) (collection: obj list) = match reader.Read() with | false -> raise (JsonSerializationException("Unexpected end when reading array")) | _ -> reader.TokenType |> function | JsonToken.Comment -> readArray reader collection | JsonToken.EndArray -> box collection | _ -> collection @ [readValue reader] |> readArray reader override __.CanConvert t = (typeof>).IsAssignableFrom t override __.WriteJson (writer:JsonWriter, value: obj, _:JsonSerializer) = writeValue writer value override __.ReadJson (reader:JsonReader, _: Type, _:obj, _:JsonSerializer) = readValue reader 

Вы не можете делать то, что я просил. По крайней мере, насколько я могу судить после MUCH исследований. Мне пришлось отредактировать источник Json.NET.

Я люблю AutoMapper и, похоже, думаю, что он решает многие проблемы … как этот …

почему бы просто не позволить JSON.NET преобразовать эту вещь во все, что она хочет … и использовать AutoMapper для сопоставления ее с объектом, который вы действительно хотите.

Если производительность не является первостепенной, этот дополнительный шаг должен стоить того, чтобы уменьшить сложность и возможность использовать нужный сериализатор.

Вы можете иметь полный контроль над сериализацией типа с помощью пользовательского JsonConverter . Документация по адресу http://james.newtonking.com/projects/json/help/html/T_Newtonsoft_Json_JsonConverter.htm .

Кроме того, согласно этому сообщению в блоге, вам нужно использовать JArray для списка и JObject для словаря.

  • Json.NET Отключить десериализацию в DateTime
  • VB.net JSON Deserialize
  • Уничтожить XML-объект с помощью динамического
  • Как реализовать пользовательский JsonConverter в JSON.NET для десериализации списка объектов базового classа?
  • Json.NET: десериализация вложенных словарей
  • Десериализация JSON с использованием JSon.NET с динамическими данными
  • Преобразование int для bool с Json.Net
  • Можно ли указать путь в атрибуте для сопоставления свойства в моем classе с дочерним свойством в моем JSON?
  • Newtonsoft JSON Deserialize
  • Как я могу разбирать JSON с C #?
  • Давайте будем гением компьютера.