Recently, I ran into a situation where I receive a list of objects in JSON from a REST endpoint. So far, this is nothing unusual and not a problem. However, the catch was that the objects were polymorphic and it was required to parse the child-class-specific fields. The previous solution was to manfully parse it. For me, that's unacceptable and I was looking for a better solution. Since I was already using Gson, I was hoping for a trick to make it work with Gson. Unfortunately, most recommended writing a custom de/serializer, which is still better than doing it manually, but also a lot of code. Finally, I found the RuntimeTypeAdapterFactory class, which solves the problem quite nicely.
Gson Series Overview
- Mapping of Enums
- Mapping of Circular References
- Generics
- Custom Serialization for Simplification (Part 1)
- Changing the Default Serialization with Custom Serialization (Part 2)
- Custom Deserialization Basics
- Custom Instance Creator
- Customizing (De)Serialization via @JsonAdapter
- Custom Deserialization for Calculated Fields
- On-The-Fly-Parsing With Streams
- ProGuard Configuration
- Low Level Parsing
- Storing Data in Android's SharedPreferences
- How to Deserialize a List of Polymorphic Objects
Example Problem
Let us assume your project uses the following classes:
public class Animal {
private String name;
private String type; // this specifies which animal it is
}
public class Dog extends Animal {
private boolean playsCatch;
}
public class Cat extends Animal {
private boolean chasesRedLaserDot;
}
}
Imagine you would get a list of Animal-Objects, which have random child classes. There are a few dogs, there are a few cats and there is no particular order.
How to Solve It
As mentioned in the introduction, the RuntimeTypeAdapterFactory.java class is the solution. It does not ship with the Gson package, so you have to download and add it manually to your project.
Next, you setup your mapping.
String responseJson = new String(responseBody); // from the service endpoint
// which format has the response of the server
final TypeToken<ServiceResponse> requestListTypeToken = new TypeToken<ServiceResponse>() {};
// adding all different container classes with their flag
final RuntimeTypeAdapterFactory<AbstractContainer> typeFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type") // Here you specify which is the parent class and what field particularizes the child class.
.registerSubtype(Dog.class, "dog") // if the flag equals the class name, you can skip the second parameter. This is only necessary, when the "type" field does not equal the class name.
.registerSubtype(Cat.class, "cat");
// add the polymorphic specialization
final Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeFactory).create();
// do the mapping
final ServiceResponse deserializedRequestList = gson.fromJson(responseJson, requestListTypeToken.getType() );
Beware, if the service returns an unexpected type, for example Horse
, Gson crashes! Nevertheless, this should give you an automatic mapping for a list of polymorphic objects with very few lines of code.
Happy coding!