Как десериализовать 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/

  • C # JSON.NET - десериализация ответа, использующего необычную структуру данных
  • JSON.Net выдает StackOverflowException при использовании
  • Удаление десериализации JSON в объект с помощью Json.NET
  • JSON.NET Parser * кажется * будет двойной сериализацией моих объектов
  • XSLT: как преобразовать XML-узел в строку
  • Игнорирование нулевых полей в Json.net
  • JSON.Net Обнаружен собственный цикл привязки
  • Как получить json.net для сериализации членов classа, полученных из List ?
  • GSON десериализует значение ключа для пользовательского объекта
  • Как сериализовать Java-примитивы с помощью Jersey REST
  • Рекомендации по сериализации объектов в пользовательский строковый формат для использования в выходном файле
  • Interesting Posts
    Давайте будем гением компьютера.