This class is from vendor library:
public class JsonParser {
public <T> T parse(String json, Class<T> type) { ... }
}
These are my models:
public class Video {
@Key
private String title;
public String getTitle() {
return title;
}
}
public class Response<TResult> {
@Key
private TResult result;
public TResult getResult() {
return result;
}
// ...
}
This code works:
JsonParser parser = new JsonParser();
String json = "{ \"title\": \"Hello world\" }";
Video video = parser.parse(json, Video.class);
This code doesn't work: (syntax error at Response<Video>.class)
JsonParser parser = new JsonParser();
String json = "{ \"result\" : { \"title\": \"Hello world\" } }";
Response<Video> videoResponse = parser.parse(reader, Response<Video>.class);
This code works:
public class VideoResponse extends Response<Video> {
}
...
JsonParser parser = new JsonParser();
String json = "{ \"result\" : { \"title\": \"Hello world\" } }";
Response<Video> videoResponse = parser.parse(reader, VideoResponse.class);
My question is: How to pass Response<Video> class to parse method as parameter without creating VideoResponse like that. (In my program, there are many models similar to Video, I dont want to duplicate my code to create empty classes VideoResponse, UserResponse, CommentResponse, ActivityResponse, etc)
Because of the way Java generics are implemented, in most cases the generic information is lost at runtime. One of the exceptions to these so-called reifiable types are concrete extensions of generic classes. For your first example:
public class Video {
@Key
private String title;
public String getTitle() {
return title;
}
}
public class Response<TResult> {
@Key
private TResult result;
public TResult getResult() {
return result;
}
// ...
}
The parser would not be able to deserialize the result property because it would be unable to determine what type it is (since this information is not available at runtime). Basically, the parse just sees java.lang.Object, and cannot determine the type to instantiate to pull the JSON data into. I assume you already suspect this to be the case, hence the attempt to make this call:
Response<Video> videoResponse = parser.parse(reader, Response<Video>.class);
In the above line you are attempting to tell the parser that the particular response is parameterized with Video, but unfortunately Java doesn't have a syntax for generic class literals, so the code doesn't compile.
In your second example:
public class VideoResponse extends Response<Video> {
}
Response<Video> videoResponse = parser.parse(reader, VideoResponse.class);
You've created a concrete extension of your generic class. For such extensions, the generic type information is available at runtime, so your parser can determine what it needs to instantiate in order to deserialize your JSON data.
All this is background information to your actual question:
My question is: How to pass Response class to parse method as parameter without creating VideoResponse like that
You neglected to mention what JSON library you are using, but in most of the popular libraries the deserialize methods have an overriden version that accepts what is commonly called a super type token. A super type token is basically just a concrete extension of a class, similar to what I described above. In Jackson, for example, you would deserialize your JSON like this:
Response<Video> response = new ObjectMapper().readValue(
jsonString, // JSON data
new TypeReference<Response<Video>>() {} // super type token, implemented by anonymous class
);
You should check your JSON libraries documentation for anything similar.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With