Deserializar la matriz de objetos JSON con Json.net

118

Intento usar una API que use la siguiente estructura de ejemplo para su json devuelto

[
   {
      "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 funcionaría muy bien con algo como la siguiente estructura

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

Pero no puedo averiguar cómo hacer que esté contento con la estructura proporcionada.

El uso del JsonConvert.DeserializeObject (contenido) predeterminado da como resultado el número correcto de Cliente, pero todos los datos son nulos.

Hacer algo en CustomerList (abajo) da como resultado una excepción "No se puede deserializar la matriz JSON actual"

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

Pensamientos

Shawn C.
fuente
¿Responde esto a tu pregunta? Deserializar JSON con C #
GetFookedWeeb

Respuestas:

187

Puede crear un nuevo modelo para deserializar su 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; }

    ...
}

Y puede deserializar su json fácilmente:

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

Espero eso ayude !

Documentación: serializar y deserializar JSON

Joffrey Kern
fuente
1
Gracias. Estaba pensando demasiado en el tema. Como respondió primero, su respuesta fue aceptada.
Shawn C.
2
JsonConvert.DeserializeObject <Lista <CustomerJson>> (json); Funciona perfecto para entradas de cuerdas.
Markel Mairs
DeserializeObject()es lento en teléfonos Android que ejecutan ARM. ¿Alguna mejor solución para ese caso?
Tadej
1
Intente navegar con un JObjectJObject.Parse(json);
Joffrey Kern
47

Para aquellos que no quieran crear ningún modelo, use el siguiente código:

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

Nota: esto no funciona para su cadena JSON. Esta no es una solución general para ninguna estructura JSON.

Tyler Long
fuente
10
Ésta es una solución terrible. En cambio, si no desea crear modelos, utilicevar result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);
a11smiles
1
@ a11smiles Explique por qué es una solución terrible.
Tyler Long
2
Primero, la asignación de memoria innecesaria para los diferentes tipos de IEnumerableimplementaciones (3 en comparación con una List <Tuple>). En segundo lugar, su solución implica dos claves distintas: 1 para cada diccionario. ¿Qué sucede si varios clientes tienen el mismo nombre? No habría diferenciación en las claves. Su solución no tiene en cuenta este conflicto.
a11smiles
2
@ a11smiles cada cliente es un diccionario separado. De modo que no habrá ningún problema incluso si hay varios clientes con el mismo nombre.
Tyler Long
1
@ a11smiles Me pregunto por qué pensaste var result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);que funcionaría. Aparentemente no funciona
Tyler Long
1

Usando la respuesta aceptada, debe acceder a cada registro usando Customers[i].customer, y necesita una CustomerJsonclase adicional , lo cual es un poco molesto. Si no desea hacer eso, puede usar lo siguiente:

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

Tenga en cuenta que estoy usando un List<>, no un Array. Ahora crea la siguiente clase:

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

Ligera modificación a lo dicho anteriormente. Mi formato Json, que valida fue

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

         }
       }

Usando la respuesta de AlexDev, hice este bucle para cada niño, creando un lector a partir de él.

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

Modificación adicional de JC_VA, tome lo que tiene y reemplace MyModelConverter con ...

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

Esto debería funcionar para json que es

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

o

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

ambos terminarán siendo analizados como si fueran

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