Десериализация массива объектов JSON с помощью Json.net

118

Я пытаюсь использовать API, который использует следующую структуру примера для возвращенного json

[
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account",
         "email":"test1@example.com",
         "organization":"",
         "reference":null,
         "id":3545134,
         "created_at":"2013-08-06T15:51:15-04:00",
         "updated_at":"2013-08-06T15:51:15-04:00",
         "address":"",
         "address_2":"",
         "city":"",
         "state":"",
         "zip":"",
         "country":"",
         "phone":""
      }
   },
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account2",
         "email":"test2@example.com",
         "organization":"",
         "reference":null,
         "id":3570462,
         "created_at":"2013-08-12T11:54:58-04:00",
         "updated_at":"2013-08-12T11:54:58-04:00",
         "address":"",
         "address_2":"",
         "city":"",
         "state":"",
         "zip":"",
         "country":"",
         "phone":""
      }
   }
]

JSON.net отлично работал бы со следующей структурой

{
    "customer": {
        ["field1" : "value", etc...],
        ["field1" : "value", etc...],
    }
}

Но я не могу понять, как сделать так, чтобы он был доволен предоставленной структурой.

Использование JsonConvert.DeserializeObject (content) по умолчанию приводит к правильному количеству клиентов, но все данные равны нулю.

Выполнение чего-либо из списка клиентов (ниже) приводит к исключению «Невозможно десериализовать текущий массив JSON».

public class CustomerList
{
    public List<Customer> customer { get; set; }
}

Мысли?

Шон С.
источник
Отвечает ли это на ваш вопрос? Десериализация JSON с помощью C #
GetFookedWeeb

Ответы:

187

Вы можете создать новую модель для десериализации вашего Json CustomerJson:

public class CustomerJson
{
    [JsonProperty("customer")]
    public Customer Customer { get; set; }
}

public class Customer
{
    [JsonProperty("first_name")]
    public string Firstname { get; set; }

    [JsonProperty("last_name")]
    public string Lastname { get; set; }

    ...
}

И вы можете легко десериализовать свой json:

JsonConvert.DeserializeObject<List<CustomerJson>>(json);

Надеюсь, поможет !

Документация: сериализация и десериализация JSON

Джоффри Керн
источник
1
Спасибо. Был над вопросом. Как вы ответили первым, ваш ответ был принят.
Шон С.
2
JsonConvert.DeserializeObject <List <CustomerJson >> (JSON); Идеально подходит для строкового ввода.
Markel
DeserializeObject()работает медленно на телефонах Android под управлением ARM. Есть ли лучшее решение для этого случая?
Тадей
1
Попробуйте ориентироваться с помощью JObjectJObject.Parse(json);
Джоффри Керн
47

Для тех, кто не хочет создавать какие-либо модели, используйте следующий код:

var result = JsonConvert.DeserializeObject<
  List<Dictionary<string, 
    Dictionary<string, string>>>>(content);

Примечание: это не работает для вашей строки JSON. Это не общее решение для любой структуры JSON.

Тайлер Лонг
источник
10
Это ужасное решение. Вместо этого, если вы не хотите создавать модели, используйтеvar result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);
a11smiles
1
@ a11smiles Пожалуйста, объясните, почему это ужасное решение.
Тайлер Лонг,
2
Во-первых, ненужное выделение памяти для разных типов IEnumerableреализаций (3 по сравнению с List <Tuple>). Во-вторых, ваше решение подразумевает два разных ключа - по 1 для каждого словаря. Что произойдет, если у нескольких клиентов одно и то же имя? По клавишам не было бы никакой разницы. Ваше решение не учитывает этот конфликт.
a11smiles
2
@ a11smiles каждому покупателю - отдельный словарь. Так что проблем не возникнет, даже если у нескольких клиентов одно и то же имя.
Тайлер Лонг,
1
@ a11smiles Мне интересно, почему вы думали, что var result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);это сработает. Очевидно, это не сработает
Тайлер Лонг
1

Используя принятый ответ, вы должны получить доступ к каждой записи с помощью Customers[i].customer, и вам нужен дополнительный CustomerJsonкласс, что немного раздражает. Если вы не хотите этого делать, вы можете использовать следующее:

public class CustomerList
{
    [JsonConverter(typeof(MyListConverter))]
    public List<Customer> customer { get; set; }
}

Обратите внимание, что я использую, а List<>не массив. Теперь создайте следующий класс:

class MyListConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var list = Activator.CreateInstance(objectType) as System.Collections.IList;
        var itemType = objectType.GenericTypeArguments[0];
        foreach (var child in token.Values())
        {
            var childToken = child.Children().First();
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(childToken.CreateReader(), newObject);
            list.Add(newObject);
        }
        return list;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
AlexDev
источник
1

Небольшая модификация того, что было сказано выше. Мой формат Json, который проверяет, был

{
    mycollection:{[
           {   
               property0:value,
               property1:value,
             },
             {   
               property0:value,
               property1:value,
             }
           ]

         }
       }

Используя ответ AlexDev, я сделал этот цикл для каждого ребенка, создав из него читателя.

 public partial class myModel
{
    public static List<myModel> FromJson(string json) => JsonConvert.DeserializeObject<myModelList>(json, Converter.Settings).model;
}

 public class myModelList {
    [JsonConverter(typeof(myModelConverter))]
    public List<myModel> model { get; set; }

}

class myModelConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var list = Activator.CreateInstance(objectType) as System.Collections.IList;
        var itemType = objectType.GenericTypeArguments[0];
        foreach (var child in token.Children())  //mod here
        {
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(child.CreateReader(), newObject); //mod here
            list.Add(newObject);
        }
        return list;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

}
JC_VA
источник
0

Дальнейшая модификация от JC_VA, возьмите то, что у него есть, и замените MyModelConverter на ...

public class MyModelConverter : JsonConverter
{
    //objectType is the type as specified for List<myModel> (i.e. myModel)
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader); //json from myModelList > model
        var list = Activator.CreateInstance(objectType) as System.Collections.IList; // new list to return
        var itemType = objectType.GenericTypeArguments[0]; // type of the list (myModel)
        if (token.Type.ToString() == "Object") //Object
        {
            var child = token.Children();
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(token.CreateReader(), newObject);
            list.Add(newObject);
        }
        else //Array
        {
            foreach (var child in token.Children())
            {
                var newObject = Activator.CreateInstance(itemType);
                serializer.Populate(child.CreateReader(), newObject);
                list.Add(newObject);
            }
        }
        return list;

    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

Это должно работать для json, который либо

myModelList{
 model: [{ ... object ... }]
}

или

myModelList{
 model: { ... object ... }
}

они оба будут проанализированы, как если бы они были

myModelList{
 model: [{ ... object ... }]
}
andmar8
источник