Desserializando a matriz de objetos JSON com Json.net

118

Estou tentando usar uma API que usa a estrutura de exemplo a seguir para seu json retornado

[
   {
      "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 funcionaria muito bem com algo como a seguinte estrutura

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

Mas não consigo descobrir como fazê-lo ficar satisfeito com a estrutura fornecida.

Usar o JsonConvert.DeserializeObject (conteúdo) padrão resulta no número correto de Cliente, mas todos os dados são nulos.

Fazer algo em CustomerList (abaixo) resulta em uma exceção "Não é possível desserializar a matriz JSON atual"

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

Pensamentos?

Shawn C.
fonte
Isso responde sua pergunta? Desserializar JSON com C #
GetFookedWeeb

Respostas:

187

Você pode criar um novo modelo para desserializar seu 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; }

    ...
}

E você pode desserializar seu json facilmente:

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

Espero que ajude !

Documentação: serializando e desserializando JSON

Joffrey Kern
fonte
1
Obrigado. Estava pensando demais no assunto. Como você respondeu primeiro, sua resposta foi aceita.
Shawn C.
2
JsonConvert.DeserializeObject <List <CustomerJson>> (json); Funciona perfeito para entradas de string.
Markel Mairs
DeserializeObject()é lento em telefones Android com ARM. Alguma solução melhor para esse caso?
Tadej
1
Tente navegar com um JObjectJObject.Parse(json);
Joffrey Kern
47

Para quem não deseja criar nenhum modelo, use o seguinte código:

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

Observação: isso não funciona para sua string JSON. Esta não é uma solução geral para qualquer estrutura JSON.

Tyler Long
fonte
10
Esta é uma solução terrível. Em vez disso, se você não quiser criar modelos, usevar result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);
a11smiles
1
@ a11smiles Explique porque é uma solução terrível.
Tyler Long
2
Primeiro, alocação de memória desnecessária para os diferentes tipos de IEnumerableimplementações (3 em comparação com List <Tuple>). Em segundo lugar, sua solução implica em duas chaves distintas - 1 para cada dicionário. O que acontecerá se vários clientes tiverem o mesmo nome? Não haveria diferenciação nas chaves. Sua solução não leva esse conflito em consideração.
a11smiles de
2
@ a11smiles cada cliente é um dicionário separado. Portanto, não haverá nenhum problema, mesmo que haja vários clientes com o mesmo nome.
Tyler Long
1
@ a11smiles Estou me perguntando por que você achou var result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);que funcionaria. Aparentemente, não funciona
Tyler Long
1

Usando a resposta aceita você tem que acessar cada registro usando Customers[i].customer, e você precisa de uma CustomerJsonaula extra , o que é um pouco chato. Se não quiser fazer isso, você pode usar o seguinte:

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

Observe que estou usando um List<>, não um Array. Agora crie a seguinte classe:

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
fonte
1

Ligeira modificação do que foi dito acima. Meu formato Json, que valida foi

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

         }
       }

Usando a resposta de AlexDev, fiz este Looping cada criança, criando um leitor a partir dele

 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
fonte
0

Modificações posteriores de JC_VA, pegue o que ele tem e substitua MyModelConverter por ...

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

Isso deve funcionar para json que seja

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

ou

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

ambos vão acabar sendo analisados ​​como se fossem

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