Десериализовать JSON с различной структурой данных

Один из JSON API, который я использую, возвращает ответ, структура данных которого меняется в зависимости от того, сколько результатов возвращается из запроса. Я использую его из С# и использую JSON.NET для десериализации ответа.

Вот JSON, который возвращается из API

Ответ с несколькими результатами:

{
  "response": {
    "result": {
      "Leads": {
        "row": [
          {
            "no": "1",
...
...
...

Ответ с одним результатом:

{
  "response": {
    "result": {
      "Leads": {
        "row": {
          "no": "1",
...
...
...

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

Вот классы, которые я использую для десериализации этих данных

Классы:

public class ZohoLeadResponseRootJson
{
    public ZohoLeadResponseJson Response { get; set; }
}

public class ZohoLeadResponseJson
{
    public ZohoLeadResultJson Result { get; set; }
}

public class ZohoLeadResultJson
{
    public ZohoDataMultiRowJson Leads { get; set; }
}

public class ZohoDataMultiRowJson
{
    public List<ZohoDataRowJson> Row { get; set; }
}

public class ZohoDataRowJson
{
    public int No { get; set; }
    ...
}

«Ответ с несколькими результатами» десериализуется без каких-либо проблем, но когда в ответе есть только один результат, из-за изменения структуры данных ответ не может быть десериализован. я получаю исключение

Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON 
object (e.g. {"name":"value"}) into type 
'System.Collections.Generic.List`1[MyNamespace.ZohoDataRowJson]' 
because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) 
or change the deserialized type so that it is a normal .NET type (e.g. not a 
primitive type like integer, not a collection type like an array or List<T>) 
that can be deserialized from a JSON object. JsonObjectAttribute can also be 
added to the type to force it to deserialize from a JSON object.

Path 'response.result.Notes.row.no', line 1, position 44.

Есть ли способ справиться с этим в Json.Net с некоторым атрибутом и, надеюсь, без необходимости писать конвертер?


person amit_g    schedule 08.09.2014    source источник
comment
Спасибо @LB за ссылку и ответ на связанный вопрос.   -  person amit_g    schedule 08.09.2014
comment
Другой подобный вопрос stackoverflow.com/questions/18994685/   -  person amit_g    schedule 08.09.2014
comment
@ФУНТ. в считывателе метода ReadJson. ValueType всегда имеет значение null. В этом случае мне пришлось использовать reader.TokenType, который является StartArray или StartObject. Кроме того, в обоих случаях десериализация будет продолжаться. Я хотел бы опубликовать ответ, так как он немного отличается от обоих связанных ответов. Я также хотел бы назвать поставщика, чтобы это могло помочь будущим пользователям столкнуться с той же проблемой.   -  person amit_g    schedule 09.09.2014
comment
Я снова открыл вопрос...   -  person L.B    schedule 09.09.2014
comment
Спасибо @L.B. Добавил ответ.   -  person amit_g    schedule 09.09.2014


Ответы (1)


Вдохновлен ответ на аналогичный вопрос.

public class ZohoDataMultiRowJson
{
    [JsonConverter(typeof(ArrayOrObjectConverter<ZohoDataRowJson>))]
    public List<ZohoDataRowJson> Row { get; set; }
}

public class ArrayOrObjectConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartArray)
        {
            return serializer.Deserialize<List<T>>(reader);
        }
        else if (reader.TokenType == JsonToken.StartObject)
        {
            return new List<T>
            {
                (T) serializer.Deserialize<T>(reader)
            };
        }
        else
        {
            throw new NotSupportedException("Unexpected JSON to deserialize");
        }
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}
person amit_g    schedule 08.09.2014
comment
Как насчет того, чтобы сделать ответ более общим, а не только для ZohoDataRowJson? Вот так было бы идеально. Кстати +1 - person L.B; 09.09.2014
comment
Имеет смысл. Обновлено. Спасибо за +1. - person amit_g; 09.09.2014