lschuetze lschuetze - 11 months ago 60
JSON Question

Gson deserialize polymorphic member List variable is null

Following is the class diagram of the problem domain. We have JSON decoded messages with different semantics which trigger different methods in the code (intialize, update) for different views.


is deserialized fine into either
using the solution proposed here using a
and registering all possible subtypes. However, the
list is empty (not deserialized). The problem is the nested polymorphic member in

Class diagram of the problem domain

The adapter factories:

RuntimeTypeAdapterFactory<Message> messageAdapterFactory = RuntimeTypeAdapterFactory
.of(Message.class, "MESSAGE_TYPE")
.registerSubtype(InitializationMessage.class, "INIT")
.registerSubtype(DataMessage.class, "DATA");

RuntimeTypeAdapterFactory<DataValue> dataAdapterFactory = RuntimeTypeAdapterFactory
.of(DataValue.class, "NAME")
.registerSubtype(DataValueA.class, "A")
.registerSubtype(DataValueB.class, "B")
.registerSubtype(DataValueC.class, "C");

The creation of the message:

TypeToken<Message> typeToken = new TypeToken<Message>() {};
Message msg = gson.fromJson(json, typeToken.getType());

DataMessage class:

public class DataMessage extends Message {

private List<DataValue> value;

public List<DataValue> getValue() {
return value;

public void setValue(List<DataValue> value) {
this.value= value;

DataValueA class:

public class DataValueA extends DataValue {

private Map<String, Float> value;

public float getValue(String location) {
return value.get(location);

The corresponding JSON:

"VALUE" : [
"NAME" : "C",
"VALUE" : 1.3
"NAME" : "A",
"VALUE" : {
"FL" : 18.4,
"FR" : 18.4,
"RL" : 18.4,
"RR" : 18.4

I want the
be deserialized into their respective subclass (

Answer Source

A solution is to use the GsonBuilder.registerTypeAdapter method to register custom JsonDeserializer. The way is to use a field in the message to define which subclass will be created (just like with RuntimeTypeAdapterFactory which is not shipped by default and lives in gson-extra).

The deserializer will be registered for each abstract superclass.

gson = new GsonBuilder()
  .registerTypeAdapter(Message.class, new MessageAdapter())
  .registerTypeAdapter(DataValue.class, new DataValueAdapter())

Given the field to distinguish subtypes is named NAME you can define the deserialize function as following. There is a mapping from the content of the field to the respective subclass.

public class DataValueAdapter implements JsonDeserializer<DataValue> {
  private final static Map<String, Class<?>> FieldToClass;

  static {
    FieldToClass = new HashMap<>();
    FieldToClass.put("PERFORMANCE", PerformanceDataValue.class);
    FieldToClass.put("TIRE_SLIP", TireSlipDataValue.class);

  public DataValue deserialize(JsonElement json, Type typeOfT,
                               JsonDeserializationContext context) throws JsonParseException {
    JsonObject jsonObject = json.getAsJsonObject();
    String dataType = jsonObject.get("NAME").getAsString();
    return context.deserialize(json, FieldToClass.get(dataType));

In order to make the reflective deserializer (which will be used for the subclasses as long as you are okay with the standard deserializer) work the subclasses need to state @SerializedName at the properties. Without it did not work for me.