Ya no necesitas indagar más por todo internet ya que llegaste al lugar indicado, tenemos la respuesta que quieres hallar pero sin problemas.
Solución:
Escribiría un deserializador personalizado que devuelva el objeto incrustado.
Digamos que su JSON es:
"status":"OK",
"reason":"some reason",
"content" :
"foo": 123,
"bar": "some value"
Entonces tendrías un Content
POJO:
class Content
public int foo;
public String bar;
Luego escribe un deserializador:
class MyDeserializer implements JsonDeserializer
@Override
public Content deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, Content.class);
Ahora, si construyes un Gson
con GsonBuilder
y registre el deserializador:
Gson gson =
new GsonBuilder()
.registerTypeAdapter(Content.class, new MyDeserializer())
.create();
Puede deserializar su JSON directamente a su Content
:
Content c = gson.fromJson(myJson, Content.class);
Editar para agregar desde los comentarios:
Si tiene diferentes tipos de mensajes pero todos tienen el campo “contenido”, puede hacer que Deserializer sea genérico haciendo lo siguiente:
class MyDeserializer implements JsonDeserializer
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, type);
Solo tienes que registrar una instancia para cada uno de tus tipos:
Gson gson =
new GsonBuilder()
.registerTypeAdapter(Content.class, new MyDeserializer())
.registerTypeAdapter(DiffContent.class, new MyDeserializer())
.create();
Cuando usted llama .fromJson()
el tipo se lleva al deserializador, por lo que debería funcionar para todos los tipos.
Y finalmente al crear una instancia de Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
La solución de @ BrianRoach es la solución correcta. Vale la pena señalar que en el caso especial en el que ha anidado objetos personalizados, ambos necesitan una TypeAdapter
, debes registrar el TypeAdapter
con el nueva instancia de GSON, de lo contrario el segundo TypeAdapter
nunca será llamado. Esto se debe a que estamos creando una nueva Gson
instancia dentro de nuestro deserializador personalizado.
Por ejemplo, si tuviera el siguiente JSON:
"status": "OK",
"reason": "some reason",
"content":
"foo": 123,
"bar": "some value",
"subcontent":
"useless": "field",
"data":
"baz": "values"
Y deseaba que este JSON se asignara a los siguientes objetos:
class MainContent
public int foo;
public String bar;
public SubContent subcontent;
class SubContent
public String baz;
Debería registrar el SubContent
‘s TypeAdapter
. Para ser más robusto, puede hacer lo siguiente:
public class MyDeserializer implements JsonDeserializer
private final Class mNestedClazz;
private final Object mNestedDeserializer;
public MyDeserializer(Class nestedClazz, Object nestedDeserializer)
mNestedClazz = nestedClazz;
mNestedDeserializer = nestedDeserializer;
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
GsonBuilder builder = new GsonBuilder();
if (mNestedClazz != null && mNestedDeserializer != null)
builder.registerTypeAdapter(mNestedClazz, mNestedDeserializer);
return builder.create().fromJson(content, type);
y luego créalo así:
MyDeserializer myDeserializer = new MyDeserializer(SubContent.class,
new SubContentDeserializer());
Gson gson = new GsonBuilder().registerTypeAdapter(Content.class, myDeserializer).create();
Esto podría usarse fácilmente para el caso de “contenido” anidado, simplemente pasando una nueva instancia de MyDeserializer
con null valores.
Un poco tarde, pero espero que esto ayude a alguien.
Simplemente cree el siguiente TypeAdapterFactory.
public class ItemTypeAdapterFactory implements TypeAdapterFactory
public TypeAdapter create(Gson gson, final TypeToken type)
final TypeAdapter delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter()
public void write(JsonWriter out, T value) throws IOException
delegate.write(out, value);
public T read(JsonReader in) throws IOException
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject())
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("content"))
jsonElement = jsonObject.get("content");
return delegate.fromJsonTree(jsonElement);
.nullSafe();
y agréguelo a su constructor GSON:
.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
o
yourGsonBuilder.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
Acuérdate de que puedes permitirte aclarar si topaste tu impedimento .