Handling Type Mismatches and Heterogeneous List Parsing with Retrofit and Gson in Android
This article explains why Retrofit + Gson parsing can fail when server‑sent fields differ from client definitions or when lists contain heterogeneous item types, and demonstrates how to customize TypeAdapters and use @JsonAdapter to gracefully handle these scenarios in Android applications.
1 Background
Android clients typically use Retrofit together with Gson for network request parsing. The minimal configuration is:
new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())While this works for most data, two problems often arise:
Server‑sent field names or types do not match the client model, causing the whole parsing to fail.
Parsing of heterogeneous list items where each element may map to a different Java class.
Heterogeneous list means that each element in the list can be represented by a different Java entity.
The article explores the causes of these issues and provides solutions.
2 Prerequisite Knowledge
Retrofit + Gson parses JSON by reading the stream with JsonReader and delegating to the appropriate TypeAdapter . The essential steps are:
// Step 1: Create a usable Retrofit instance
new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
// Step 2: Add a list of TypeAdapters
public static GsonConverterFactory create() {
return create(new Gson());
}
// Gson() constructor registers built‑in TypeAdapters
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);
factories.add(TypeAdapters.STRING_FACTORY);
... // other factories
// Step 3: When a response arrives, GsonConverterFactory intercepts it
@Override
public Converter
responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter
adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
// Step 4: Use the corresponding TypeAdapter to complete parsing
public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}These four snippets show that Retrofit + Gson reads the JSON string with JsonReader and uses the matching TypeAdapter for conversion.
3 Handling Server‑Client Field Type Incompatibility
By default, a mismatched field throws an exception and aborts parsing. To tolerate such mismatches, the read method of ReflectiveTypeAdapterFactory can be modified to skip problematic fields:
public T read(JsonReader in) throws IOException {
try {
while (in.hasNext()) {
String name = in.nextName();
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
in.skipValue();
} else {
try {
field.read(in, instance);
} catch (Exception e) {
in.skipValue(); // skip the offending field
}
}
}
} catch (Exception e) {
// Optional: log the error
in.skipValue();
}
in.endObject();
return instance;
}This change catches any exception during field.read , skips the offending value, and continues parsing, preventing a total failure.
4 Heterogeneous List Parsing
Consider a JSON array where each element contains a type field indicating its concrete class:
{
"list": [
{"type": "a", ...},
{"type": "b", ...},
{"type": "c", ...}
]
}Define a base class BaseCard with a cardType field, and let ACard , BCard , CCard extend it:
public class BaseCard {
@SerializedName("type")
private String cardType;
}
public class ACard extends BaseCard { }
public class BCard extends BaseCard { }
public class CCard extends BaseCard { }Create a custom TypeAdapterFactory that maps each type value to the corresponding TypeAdapter and registers it in a map:
protected
void addSubTypeAdapter(TypeAdapterFactory factory, Gson gson,
String typeName, Class
subTypeClass) {
mMap.put(typeName, new SubTypeReadWriteAdapter<>(
gson.getDelegateAdapter(factory, TypeToken.get(subTypeClass))));
}
public final
TypeAdapter
create(Gson gson, TypeToken
type) {
return (TypeAdapter
) createTypeAdapter(gson);
}
public TypeAdapter
createTypeAdapter(final Gson gson) {
final TypeAdapterFactory factory = this;
return new BaseTypeAdapter
(gson, "type") {
{
addSubTypeAdapter(factory, gson, "a", ACard.class);
addSubTypeAdapter(factory, gson, "b", BCard.class);
addSubTypeAdapter(factory, gson, "c", CCard.class);
}
};
}Annotate the base class with @JsonAdapter(DemoTypeAdapterFactory.class) so that Gson uses the custom factory:
@JsonAdapter(DemoTypeAdapterFactory.class)
public class BaseCard { ... }With this setup, Gson can correctly deserialize a heterogeneous list by selecting the appropriate subclass based on the type field.
Overall, the article demonstrates how to extend Retrofit + Gson parsing to handle field‑type mismatches gracefully and to parse lists containing multiple concrete types using custom TypeAdapter implementations.
Beike Product & Technology
As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.