Mobile Development 9 min read

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.

Beike Product & Technology
Beike Product & Technology
Beike Product & Technology
Handling Type Mismatches and Heterogeneous List Parsing with Retrofit and Gson in Android

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.

AndroidParsingJSONGsonRetrofitHeterogeneous ListTypeAdapter
Beike Product & Technology
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.