GSON ignorar elementos con tipo incorrecto

Estoy utilizando Retrofit (en combinación con OkHttp y GSON) para comunicarse con un servicio web en línea. El webservice tiene un contenedor predeterminado alrededor de todas sus respuestas, similar a:

{ "resultCode":"OK", "resultObj":"Can be a string or JSON object / array", "error":"", "message":"" } 

En este ejemplo resultCode será bien o NO . Además, el error y el message sólo tienen contenido cuando se ha producido un error durante el procesamiento de la solicitud. Y por último, pero no menos importante, resultObj contendrá el resultado real de la llamada (que es una cadena en el ejemplo, pero algunas llamadas devuelven una matriz JSON o un objeto JSON).

Para procesar estos metadatos, creé una clase genérica, como ésta:

 public class ApiResult<T> { private String error; private String message; private String resultCode; private T resultObj; // + some getters, setters etcetera } 

También he creado clases que representan las respuestas a veces dadas en resultObj y he definido una interfaz para usar con Retrofit, que se ve un poco como esto:

 public interface SomeWebService { @GET("/method/string") ApiResult<String> someCallThatReturnsAString(); @GET("/method/pojo") ApiResult<SomeMappedResult> someCallThatReturnsAnObject(); } 

Siempre y cuando la solicitud es válida todo funciona bien. Pero cuando se produce un error en el lado del servidor, todavía devolverá un resultObj con un String-type. Esto hace que someCallThatReturnsAnObject bloquee dentro de la biblioteca RetroAdapter / GSON, con un mensaje como este:

Retrofit.RetrofitError: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:

Se esperaba BEGIN_OBJECT pero era STRING en la línea 1 de la columna 110 path $ .resultObj

Ahora, finalmente, mis preguntas son:

  1. ¿Hay una manera (fácil) de decirle a GSON que debería ignorar (aka "anular") una propiedad si no coincide con el tipo esperado?
  2. ¿Puedo decirle a GSON que trate cuerdas vacías como nulas?

Defina su modelo como este:

 public class ApiResult { private String error; private String message; private String resultCode; private MyResultObject resultObj; } 

A continuación, cree un TypeAdapterFactory para MyResultObject :

 public class MyResultObjectAdapterFactory implements TypeAdapterFactory { @Override @SuppressWarnings("unchecked") public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { if (type.getRawType()!= MyResultObject.class) return null; TypeAdapter<MyResultObject> defaultAdapter = (TypeAdapter<MyResultObject>) gson.getDelegateAdapter(this, type); return (TypeAdapter<T>) new MyResultObjectAdapter(defaultAdapter); } public class MyResultObjectAdapter extends TypeAdapter<MyResultObject> { protected TypeAdapter<MyResultObject> defaultAdapter; public MyResultObjectAdapter(TypeAdapter<MyResultObject> defaultAdapter) { this.defaultAdapter = defaultAdapter; } @Override public void write(JsonWriter out, MyResultObject value) throws IOException { defaultAdapter.write(out, value); } @Override public MyResultObject read(JsonReader in) throws IOException { /* This is the critical part. So if the value is a string, Skip it (no exception) and return null. */ if (in.peek() == JsonToken.STRING) { in.skipValue(); return null; } return defaultAdapter.read(in); } } } 

Finalmente, registre MyResultObjectAdapterFactory para Gson :

 Gson gson = new GsonBuilder() .registerTypeAdapterFactory(new MyResultObjectAdapterFactory()) .create(); 

Ahora, al deserializar un json de ApiResult con ese objeto Gson , resultObj se establecerá null si es una cadena .

Espero que esto solucione su problema =)

He tenido un problema similar y llegó con la siguiente solución al final:

En lugar de intentar analizar su elemento en una String o Array , trate de almacenar los datos en un simple java.lang.Object

Esto evita que el análisis se bloquee o genere una excepción.

p.ej. Con anotaciones GSON la propiedad de su modelo sería así:

 @SerializedName("resultObj") @Expose private java.lang.Object resultObj; 

A continuación, al acceder a sus datos en tiempo de ejecución, puede comprobar si su propiedad resultObj es una instancia de String o no.

 if(apiResultObject instanceof String ){ //Cast to string and do stuff } else{ //Cast to array and do stuff } 

Publicación original: https://stackoverflow.com/a/34178082/3708094

En primer lugar, se trata de un diseño de la API mal que se trata. Unesdoc.unesco.org

Puede utilizar un JsonDeserializer personalizado para manejar este caso.

Registrarlo con Retrofit:

 MyJsonDeserializer deserializer = new MyJsonDeserializer()).create(); final Gson gson = new GsonBuilder().registerTypeAdapter(ApiResult.class, deserializer); RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(API_URL) .setConverter(new GsonConverter(gson)) .build(); 
  • Cómo llamar al método GET simple usando "Retrofit"
  • ProGuard no funciona con okhttp
  • Retrofit 2 + Rxjava error de manipulación
  • Manejo de errores personalizados con Retrofit cuando se ofusca usando proguard da java.lang.reflect.UndeclaredThrowableException
  • Manejo de excepciones API en RxJava
  • Obtención de encabezado de respuesta (Retrofit / OkHttp Client)
  • Encadenamiento de servicios de Retrofit con soporte de RxJava
  • Actualizaciones de Android Retrofit 2.0
  • Retrofit Image Upload dar error
  • HTTPS no envía algunos encabezados de autenticación con Retrofit
  • Cómo enviar datos JSON como Cuerpo usando Retrofit android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.