Maybe you want to use GSON's RuntimeTypeAdapterFactory. It allows you to determine the type of class to be deserialized automatically from type field: conveniently named as type by default.
To have inheritance right for GSON to deserialize I suggest small changes to your POJOs. Create to class for Request
like:
@Getter @Setter
public class Request {
private String type;
@Getter @Setter
public static class Content {
String password;
}
}
Override it and Content per request type, like:
@Getter @Setter
public class ForwardRequest extends Request {
@Getter @Setter
public static class ForwardContent extends Content {
private String deviceId;
}
private ForwardContent content;
}
and
@Getter @Setter
public class LoginRequest extends Request {
@Getter @Setter
public static class LoginContent extends Content {
private String username;
}
private LoginContent content;
}
With classes like above it is just:
@Test
public void test() {
// Tell the top class
RuntimeTypeAdapterFactory<Request> rttaf = RuntimeTypeAdapterFactory.of(Request.class)
// Register inheriting types and the values per type field
.registerSubtype(ForwardRequest.class, "Forward")
.registerSubtype(LoginRequest.class, "Login");
Gson gson = new GsonBuilder().setPrettyPrinting()
.registerTypeAdapterFactory(rttaf)
.create();
// Constructed an array of your two types to be deserialized at the same time
String jsonArr = "["
+ "{\"type\": \"Login\", \"content\": {\"username\": \"a\", \"password\": \"b\"}}"
+ ","
+ "{\"type\": \"Forward\", \"content\": {\"deviceId\": \"a\", \"password\": \"b\"}}"
+ "]";
// Deserialize the array as Request[]
Request[] requests = gson.fromJson(jsonArr, Request[].class);
log.info("{}", requests[0].getClass());
log.info("{}", requests[1].getClass());
}
Outpur for the above would be similar to:
class org.example.gson.LoginRequest
class org.example.gson.ForwardRequest
You just need to copy the file provided in the link at the top of answer to include RuntimeTypeAdapterFactory
into your project.
Update: About deserializing type
or any other field that contains the information about type: GSON leaves it out on purpose. It is a kind of a transient field is it not? You need to know the value of type
before you can deserialize so GSON does not bother to deserialize it anymore.
As an another example - to clarify this a bit - if you change the test string like:
String jsonArr = "["
+ "{\"type\": \"LoginRequest\", \"content\": {\"username\": \"a\", \"password\": \"b\"}}"
+ ","
+ "{\"type\": \"ForwardRequest\", \"content\": {\"deviceId\": \"a\", \"password\": \"b\"}}"
+ "]";
so that type holds simple names of classes (which is usually the
case) you can register subtypes just like:
.registerSubtype(ForwardRequest.class)
.registerSubtype(LoginRequest.class);
and GSON would expect class simple name as a value for JSON's type attribute. Why would you hold class name in separate field because it is gettable Class.getSimpleName()
?
However you of course might sometimes need to serialize it to other clients.