Как десериализовать JSON с двойными именами свойств в одном и том же объекте

У меня есть строка JSON, которую я ожидаю содержать дублирующиеся ключи, с которыми я не могу довести JSON.NET.

Мне было интересно, знает ли кто-нибудь лучший способ (может быть, использовать JsonConverter ?), Чтобы заставить JSON.NET изменить JObject JObjects в JArrays когда он видит повторяющиеся имена ключей?

 // For example: This gives me a JObject with a single "JProperty\JObject" child. var obj = JsonConvert.DeserializeObject("{ \"HiThere\":1}"); // This throws: // System.ArgumentException : Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject. obj = JsonConvert.DeserializeObject("{ \"HiThere\":1, \"HiThere\":2, \"HiThere\":3 }"); 

Фактический JSON, который я пытаюсь десериализовать, намного сложнее, и дубликаты вложены на несколько уровней. Но приведенный выше код демонстрирует, почему он терпит неудачу для меня.

Я понимаю, что JSON неверен, поэтому я спрашиваю, есть ли у JSON.NET способ обойти это. Для аргументации, скажем, у меня нет контроля над JSON. Я действительно использую определенный тип для родительского объекта, но конкретное свойство, которое имеет проблемы, будет либо строкой, либо другим вложенным объектом JSON. По этой причине тип свойства failing является «объектом».

Интересный вопрос. JObject время я JObject с этим и обнаружил, что, хотя JObject не может содержать свойства с повторяющимися именами, JsonTextReader используемый для заполнения его во время десериализации, не имеет такого ограничения. (Это имеет смысл, если вы думаете об этом: это читатель, ориентированный только на будущее, он не имеет отношения к тому, что он читал в прошлом). Вооружившись этими знаниями, я сделал попытку написать код, который заполнит иерархию JTokens, при необходимости преобразуя значения свойств в JArrays, если в конкретном объекте JObject встречается дублирующее имя свойства. Поскольку я не знаю вашего фактического JSON и требований, вам может потребоваться внести некоторые корректировки, но с чего-то начать, по крайней мере.

Вот код:

 public static JToken DeserializeAndCombineDuplicates(JsonTextReader reader) { if (reader.TokenType == JsonToken.None) { reader.Read(); } if (reader.TokenType == JsonToken.StartObject) { reader.Read(); JObject obj = new JObject(); while (reader.TokenType != JsonToken.EndObject) { string propName = (string)reader.Value; reader.Read(); JToken newValue = DeserializeAndCombineDuplicates(reader); JToken existingValue = obj[propName]; if (existingValue == null) { obj.Add(new JProperty(propName, newValue)); } else if (existingValue.Type == JTokenType.Array) { CombineWithArray((JArray)existingValue, newValue); } else // Convert existing non-array property value to an array { JProperty prop = (JProperty)existingValue.Parent; JArray array = new JArray(); prop.Value = array; array.Add(existingValue); CombineWithArray(array, newValue); } reader.Read(); } return obj; } if (reader.TokenType == JsonToken.StartArray) { reader.Read(); JArray array = new JArray(); while (reader.TokenType != JsonToken.EndArray) { array.Add(DeserializeAndCombineDuplicates(reader)); reader.Read(); } return array; } return new JValue(reader.Value); } private static void CombineWithArray(JArray array, JToken value) { if (value.Type == JTokenType.Array) { foreach (JToken child in value.Children()) array.Add(child); } else { array.Add(value); } } 

И вот демо:

 class Program { static void Main(string[] args) { string json = @" { ""Foo"" : 1, ""Foo"" : [2], ""Foo"" : [3, 4], ""Bar"" : { ""X"" : [ ""A"", ""B"" ] }, ""Bar"" : { ""X"" : ""C"", ""X"" : ""D"" }, }"; using (StringReader sr = new StringReader(json)) using (JsonTextReader reader = new JsonTextReader(sr)) { JToken token = DeserializeAndCombineDuplicates(reader); Dump(token, ""); } } private static void Dump(JToken token, string indent) { Console.Write(indent); if (token == null) { Console.WriteLine("null"); return; } Console.Write(token.Type); if (token is JProperty) Console.Write(" (name=" + ((JProperty)token).Name + ")"); else if (token is JValue) Console.Write(" (value=" + token.ToString() + ")"); Console.WriteLine(); if (token.HasValues) foreach (JToken child in token.Children()) Dump(child, indent + " "); } } 

Вывод:

 Object Property (name=Foo) Array Integer (value=1) Integer (value=2) Integer (value=3) Integer (value=4) Property (name=Bar) Array Object Property (name=X) Array String (value=A) String (value=B) Object Property (name=X) Array String (value=C) String (value=D) 

Брайан Роджерс – Вот вспомогательная функция JsonConverter, которую я написал. Я изменил его на основе ваших комментариев о том, как JsonTextReader является просто передовым читателем, не заботятся о повторяющихся значениях.

 private static object GetObject(JsonReader reader) { switch (reader.TokenType) { case JsonToken.StartObject: { var dictionary = new Dictionary(); while (reader.Read() && (reader.TokenType != JsonToken.EndObject)) { if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException("Unknown JObject conversion state"); string propertyName = (string) reader.Value; reader.Read(); object propertyValue = GetObject(reader); object existingValue; if (dictionary.TryGetValue(propertyName, out existingValue)) { if (existingValue is List) { var list = existingValue as List; list.Add(propertyValue); } else { var list = new List {existingValue, propertyValue}; dictionary[propertyName] = list; } } else { dictionary.Add(propertyName, propertyValue); } } return dictionary; } case JsonToken.StartArray: { var list = new List(); while (reader.Read() && (reader.TokenType != JsonToken.EndArray)) { object propertyValue = GetObject(reader); list.Add(propertyValue); } return list; } default: { return reader.Value; } } } 

Вы не должны использовать общий тип объекта, он должен быть более конкретным типом.

Тем не менее, вы ошибаетесь, что это проблема rmain

У тебя есть :

 "{ \"HiThere\":1, \"HiThere\":2, \"HiThere\":3 }" 

Но это должно быть:

 "{"HiTheres": [{\"HiThere\":1}, {\"HiThere\":2}, {\"HiThere\":3} ]}" 

Или

 "{ \"HiThereOne\":1, \"HiThereTwo\":2, \"HiThereThree\":3 }" 

Вы json – это один объект с тремя полями с одинаковым именем («HiThere»). Какой из них не будет работать.

Json, который я показал, дает: массив (HiTheres) из трех объектов, каждый с полем объекта HiThere Or One с тремя полями с разными именами. (HiThereOne, HiThereTwo, «HiThereThree»)

Взгляните на http://jsoneditoronline.org/index.html И http://json.org/

  • Сохранение / загрузка данных в Unity
  • Сериализовать компоненты JavaFX
  • Игнорирование нулевых полей в Json.net
  • Сериализация типа Json.Net с полиморфным дочерним объектом
  • Json Сериализация Java, которая работает с GWT
  • ТипNameHandling предостережение в Newtonsoft Json
  • GSON десериализует значение ключа для пользовательского объекта
  • Каковы различия между XmlSerializer и BinaryFormatter
  • Java: объект для байта и байт для конвертера объектов (для Tokyo Cabinet)
  • Серийный цикл ActiveRecord с использованием JSON вместо YAML
  • Json.net сериализует определенное частное поле
  • Interesting Posts

    Как изменить FileZilla, чтобы View / Edit открывал .html в Notepad ++, а не Firefox

    Проверка на стороне сервера версии 3 для покупок в приложении Google Play

    Как сохранить img на локальный компьютер пользователя с помощью HTML2canvas

    Как повысить предупреждение, если возвращаемое значение не учитывается?

    Использование npm за корпоративным прокси .pac

    Как отформатировать диск, зашифрованный с помощью Bitlocker?

    Подробная информация о USB-не повезло до сих пор

    Обновление GCC на OSX

    Автоматическая перезагрузка Windows8, если нет активности в Интернете

    Как сгруппировать RadioButton из разных LinearLayouts?

    Как настроить собственный сервер GIT? Что такое голые / не-голые репозитории?

    Возможно ли объединить два интернет-соединения для повышения производительности?

    Как запустить Tensorflow на CPU

    Транспонирование данных из одного списка столбцов в несколько столбцов

    Как получить доступ к моему домашнему ПК из офиса?

    Давайте будем гением компьютера.