Deserialización GSON con tipos genéricos y nombres genéricos de campos

Digamos que tenemos una estructura como esta:

JSON:

{ "body": { "cats": [{ "cat": { "id": 1, "title": "cat1" } }, { "cat": { "id": 2, "title": "cat2" } }] } } 

Y POJO correspondiente:

Clase de respuesta

private final Body body;

Clase corporal

private final Collection<CatWrapper> cats

CatWrapper.class

private final Cat cat

Clase de gato

 private final int id; private final String title; 

Pero digamos ahora que tenemos la misma estructura, pero en vez de Cat recibimos truck

 { "body": { "trucks": [{ "truck": { "id": 1, "engine": "big", "wheels" : 12 } }, { "truck": { "id": 2, "engine": "super big", "wheels" : 128 } }] } } 

Estoy usando GSON (por defecto retrofit json parser) en Android , considere esto al dar soluciones.

Podríamos usar genéricos aquí para que la respuesta se vea así:

private final Body<ListResponse<ItemWrapper<T>> pero no podemos, ya que los nombres de campo son específicos de una clase.

PREGUNTA:

¿Qué puedo hacer para serializarlo de forma automática sin crear tantas clases clásicas? Realmente no necesito clases separadas como BodyCat , BodyTruck , BodyAnyOtherClassInThisStructure y estoy buscando una manera de evitar tenerlos.

@EDITAR:

He cambiado clases (gato, perro -> gato, camión) debido a la confusión de la herencia, las clases usadas aquí como ejemplo NO se extiende una a otra

Una idea sería definir un deserializador genérico personalizado. Su tipo genérico representará la clase concreta de los elementos de la lista envueltos en una instancia de Body .

Asumiendo las siguientes clases:

 class Body<T> { private List<T> list; public Body(List<T> list) { this.list = list; } } class Cat { private int id; private String title; ... } class Truck { private int id; private String engine; private int wheels; ... } 

El deserializador asume que la estructura del json es siempre la misma, en el sentido de que usted tiene un objeto que contiene un objeto llamado "body". También asume que el valor en el primer par de valores-clave de este cuerpo es una lista.

Ahora, para cada elemento de la matriz json, necesitamos buscar de nuevo el objeto interno asociado con cada clave. Deserializamos este valor y lo ponemos en la lista.

 class CustomJsonDeserializer<T> implements JsonDeserializer<Body<T>> { private final Class<T> clazz; public CustomJsonDeserializer(Class<T> clazz) { this.clazz = clazz; } @Override public Body<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject body = json.getAsJsonObject().getAsJsonObject("body"); JsonArray arr = body.entrySet().iterator().next().getValue().getAsJsonArray(); List<T> list = new ArrayList<>(); for(JsonElement element : arr) { JsonElement innerElement = element.getAsJsonObject().entrySet().iterator().next().getValue(); list.add(context.deserialize(innerElement, clazz)); } return new Body<>(list); } } 

Para el paso final, sólo tiene que crear el tipo correspondiente, instanciar y registrar el adaptador en el analizador. Por ejemplo para camiones:

 Type truckType = new TypeToken<Body<Truck>>(){}.getType(); Gson gson = new GsonBuilder() .registerTypeAdapter(truckType, new CustomJsonDeserializer<>(Truck.class)) .create(); Body<Truck> body = gson.fromJson(new FileReader("file.json"), truckType); 

Incluso puede devolver la lista directamente desde el adaptador si desea deshacerse de la clase Body .

Con los camiones obtendrá [1_big_12, 2_super big_128] como salida y [1_cat1, 2_cat2] con los gatos.

Yo estaría utilizando este enfoque:

 public class Body { private List<Animal> animals; } } public class Animal {} public class Dog extends Animal {} public class Cat extends Animal {} 

En este caso usted tendrá la serialización w / o cualquier boilerplates, excepto hecho que usted tiene que utilizar Gson TypeAdapter para Animal clase Animal , como:

 Gson gson = new GsonBuilder() .registerTypeAdapter(Animal.class, new AnimalSerializer()) .create(); 

Donde TypeAdapter debería verse como:

 public class AnimalSerializer implements JsonSerializer<Animal>, JsonDeserializer<Animal> { private static final String CLASS_META_KEY="clz"; @Override public JsonElement serialize(Animal src, Type typeOfSrc, JsonSerializationContext context) { JsonElement element=null; if (src == null) { return element; } if(src instanceof Cat) element = context.serialize(src, Cat.class); else if(src instanceof Dog) element = context.serialize(src, Dog.class); else throw new IllegalArgumentException("Unspecifiad class serializer for "+src.getClass().getName()); element.getAsJsonObject().addProperty(CLASS_META_KEY, src.getClass().getCanonicalName()); return element; } @Override public Field deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Class<?> clz; Animal animal; JsonObject object = jsonElement.getAsJsonObject(); if (object.has(CLASS_META_KEY)) { String className = object.get(CLASS_META_KEY).getAsString(); try { clz = Class.forName(className); } catch (Exception e) { Log.e(TAG, "Can't deserialize class="+className,e); clz = Animal.class; } animal = context.deserialize(jsonElement, clz); } else { animal = context.deserialize(jsonElement, typeOfT); } return animal; } } 
 public class body { private List<cats> cats; private List<dogs> dogs; public class cats { private list<cat> cat; } public class dogs { private list<dog> dog; } } 

Esto realmente no reduce el código tradicional, pero debe impedir que tenga una clase de cuerpo separado con listas de clases que son sólo listas de sus animales reales. Debe hacer para que usted apenas tenga su clase del cuerpo, y entonces una clase para cada animal con él es stats.

  • Retrofit 2 (en Android) después de la implementación, ¿Dónde están mis elementos JSON?
  • Protobuf streaming (serialización perezosa) API
  • JSONException: El valor del tipo java.lang.String no se puede convertir en JSONObject
  • Asignación de un JSONObject a una lista con Flexjson en Java / Android
  • Obtenga String de json con un objeto json anidado y matrices json anidadas con varios objetos json, en Android
  • Http Get Request devuelve html?
  • Cómo analizar la matriz json anidado en android utilizando volley biblioteca
  • Mi actividad no puede conectarse al sitio web y mostrar los datos de JSON mediante volley
  • Prueba de objetos JSON nulos en Java / Android
  • Deserializar JSON a Java enum
  • Twitter JSON API en el desarrollo de Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.