JorgeMuci JorgeMuci - 2 months ago 31
Java Question

Deserializing polymorphic JSON with GSON throwing exception

I'm working on an app that is using GSON as json deserializer and needs to deserialize polymorphic json from REST API. Before explaining mi issue note that I've already been looking around polymorphic deserialization with GSON and have implemented it on several cases successfully. So this is a specific issue I'm having. Also I've read this great post and this stackoverflow discussion before asking this. I'm using RuntimeTypeAdapterFactory to deserialize polymorphic objects by the way.

The problem I'm having is that apparently GSON's RuntimeTypeAdapterFactory does't allow to declare the field which specifies the type of the object inside the hierarchy structure. I'll explain further with some code. I have the following pojos structure (pojos had been reduced for the sake of simplicity):

public abstract class BaseUser {
@Expose
protected EnumMobileUserType userType;
}


public class User extends BaseUser {
@Expose
private String name;
@Expose
private String email;
}

public class RegularUser extends User {
@Expose
private String address;
}

public class SpecialUser extends User {
@Expose
private String promoCode;
}


Now this is the code where I defined the runtimeTypeAdapterFactory for User hierarchy.

public static RuntimeTypeAdapterFactory<BaseUser> getUserTypeAdapter() {
return RuntimeTypeAdapterFactory
.of(BaseUser.class, "userType")
.registerSubtype(User.class, EnumMobileUserType.USER.toString())
.registerSubtype(RegularUser.class, EnumMobileUserType.REGULAR.toString())
.registerSubtype(SpecialUser.class, EnumMobileUserType.SPECIAL.toString());
}

public static Gson getGsonWithTypeAdapters() {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapterFactory(getUserTypeAdapter());
return builder.create();
}


Now when I try to deserialize a json:

{
"user":{
"userType":"USER",
"email":"albert@gmail.com",
"name":"Albert"
}
}


I get this exception

com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType


But if I change the name of the property "userType" in my BaseUser class to "type" for example and I deserialize the same json everything properly. I don't get why GSON runtimTypeAdapterFactory has this restriction. In fact in this blog post apparently this is not an issue.

Can anyone explain what's going on here, why the name of the property that defines the type cannot be defined inside the pojos hierarchy?

EDIT the issue is not when deserializing but when serializing using the code described above. Find further explanation in the answer.

Answer

Well, after some time digging I found out that the problem is not actually deserializing, the problem comes when serializing and having a RuntimeTypeFactory registered as described in the question. If you register a runtimeTypeAdapterFactory and use the same field name to define the class type in the factory and in your pojo, json resulting from serializing pojo to json using GSON with the RuntimeTypeAdapterFactory for a SpecialUser for example will be:

{  
  "user":{  
      "userType":"SPECIAL",
      "email":"albert@gmail.com",
      "name":"Albert"
      "userType":"SPECIAL"
  }
}

This will result in the exception described:

com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType

because de field userType is repeated in the json due to GSON serializer, which will automatically add a field as declared in the RuntimeTypeAdapterFactory registered for the class BaseUser.