Deserializacja tablicy obiektów JSON za pomocą Json.net

118

Próbuję użyć interfejsu API, który używa następującej struktury przykładu dla ich zwróconego pliku json

[
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account",
         "email":"[email protected]",
         "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":"[email protected]",
         "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 działałby świetnie z czymś w rodzaju poniższej struktury

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

Ale nie potrafię wymyślić, jak sprawić, by był zadowolony z dostarczonej struktury.

Użycie domyślnego JsonConvert.DeserializeObject (content) skutkuje poprawną liczbą Customer, ale wszystkie dane są puste.

Wykonanie czynności CustomerList (poniżej) powoduje wyjątek „Nie można deserializować bieżącej tablicy JSON”

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

Myśli?

Shawn C.
źródło
Czy to odpowiada na twoje pytanie? Deserializacja JSON z C #
GetFookedWeeb

Odpowiedzi:

187

Możesz utworzyć nowy model do deserializacji swojego 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; }

    ...
}

I możesz łatwo deserializować swój json:

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

Mam nadzieję, że to pomoże !

Dokumentacja: serializacja i deserializacja JSON

Joffrey Kern
źródło
1
Dzięki. Był zbyt przemyślany. Jak odpowiedziałeś jako pierwszy, twoja odpowiedź została zaakceptowana.
Shawn C.
2
JsonConvert.DeserializeObject <List <CustomerJson>> (json); Działa idealnie dla wejść stringowych.
Markel Mairs
DeserializeObject()działa wolno na telefonach z systemem Android z procesorem ARM. Jakieś lepsze rozwiązanie w tej sprawie?
Tadej
1
Spróbuj nawigować z JObjectemJObject.Parse(json);
Joffrey Kern
47

Dla tych, którzy nie chcą tworzyć żadnych modeli, użyj następującego kodu:

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

Uwaga: to nie działa w przypadku Twojego ciąg JSON. To nie ogólne rozwiązanie dla każdej struktury JSON.

Tyler Long
źródło
10
To straszne rozwiązanie. Zamiast tego, jeśli nie chcesz tworzyć modeli, użyjvar result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);
a11smiles
1
@ a11smiles Proszę wyjaśnić, dlaczego jest to okropne rozwiązanie.
Tyler Long
2
Po pierwsze, niepotrzebna alokacja pamięci dla różnych typów IEnumerableimplementacji (3 w porównaniu z List <Tuple>). Po drugie, twoje rozwiązanie implikuje dwa różne klucze - po 1 dla każdego słownika. Co się stanie, jeśli wielu klientów ma to samo imię? Na klawiszach nie byłoby rozróżnienia. Twoje rozwiązanie nie bierze pod uwagę tego konfliktu.
a11smiles
2
@ a11smiles każdy klient to osobny słownik. Nie będzie więc problemu, nawet jeśli wielu klientów ma to samo imię.
Tyler Long
1
@ a11smiles Zastanawiam się, dlaczego według Ciebie var result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);zadziała. Najwyraźniej to nie działa
Tyler Long
1

Korzystając z zaakceptowanej odpowiedzi, musisz uzyskać dostęp do każdego rekordu za pomocą Customers[i].customeri potrzebujesz dodatkowej CustomerJsonklasy, co jest trochę denerwujące. Jeśli nie chcesz tego robić, możesz skorzystać z:

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

Zwróć uwagę, że używam a List<>, a nie Array. Teraz utwórz następującą klasę:

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
źródło
1

Nieznaczna modyfikacja tego, co stwierdzono powyżej. Mój format Json, który sprawdza poprawność to

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

         }
       }

Korzystając z odpowiedzi AlexDev, zrobiłem to zapętlając każde dziecko, tworząc z niego czytnik

 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
źródło
0

Dalsza modyfikacja od JC_VA, weź to, co ma i zamień MyModelConverter na ...

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();
}

To powinno zadziałać w przypadku json

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

lub

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

oba zostaną przeanalizowane tak, jakby były

myModelList{
 model: [{ ... object ... }]
}
andmar8
źródło