I am trying to write a generic method for deserializing json into my model. My problem is that I don't know how to get Class from the generic type T. My code looks something like this (and doesn't compile this way)
public class JsonHelper {
public <T> T Deserialize(String json)
{
Gson gson = new Gson();
return gson.fromJson(json, Class<T>);
}
}
I tried something else, to get the type, but it throws an error I had the class as JsonHelper<T> and then tried this
Class<T> persistentClass = (Class<T>) ((ParameterizedType)getClass()
.getGenericSuperclass())
.getActualTypeArguments()[0];
The method signature looks like this
com.google.gson.Gson.fromJson(String json, Class<T> classOfT)
So, how can I translate along T so that when I call JsonHelper.Deserialize<MyObject>(json); I get an instance of the correct object?
You need to get a Class instance from somewhere. That is, your Deserialize() method needs to take a Class<T> as a parameter, just like the underlying fromJson() method.
Your method signature should look like Gson's:
<T> T Deserialize(String json, Class<T> type) ...
Your calls will look like this:
MyObject obj = helper.Deserialize(json, MyObject.class);
By the way, the convention to start method names with a lowercase letter is well established in Java.
Unfortunately, the way Java handles generics, you cannot get the class like you're asking. That's why Google's stuff asks specifically for the class as an argument. You'll have to modify your method signature to do the same.
Related
I'm trying to create a CSV message converter to use with the Spring message converters but I'm not able to configure the support(Class<?> clazz) method when clazz is of type Collection. Basically my REST endpoint is returning a Collection of objects directly and not wrapped inside another object.
Using reflection, the only thing I'm able to get as the type of the Collection is E which obviously doesn't give me much to work with.
I've tried manually and also with helper tools like TypeUtils.genericArrayType(clazz) from Apache Commons.
The only work around I can think of is to just always return true and verify the type inside the writeInternal(Object o, HttpOutputMessage message) method.
Is this a bug in the implementation of the AbstractMessageConverterMethodProcessor class that retrieves the return type of the endpoint or am I missing something?
By the way, this is the method that returns the type in the Spring implementation?
protected Class<?> getReturnValueType(Object value, MethodParameter returnType) {
return (value != null ? value.getClass() : returnType.getParameterType());
}
Well, I think I found the answer. And it's basically Java's type erasure that deletes the Collection type information at runtime. There is another abstract class AbstractGenericHttpMessageConverter which lets me implement public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) and protected void writeInternal(Object o, Type type, HttpOutputMessage outputMessage) methods which contain a Type argument that can be used to determine the exact Class of the object.
I am using fasterxml.jackson. I am confused about readValue(). Here is my question.
I know jackson deserialize normal JavaBean and Collection in two different ways.
For JavaBean, we can pass MyBean.class or new TypeReference<MyBean> to readValue(). For Collections, we must pass new TypeReference<List<MyBean>>. That is because TypeReference saves the type erased by Collection. Am I right? :)
Now I am confused. If MyBean contains a list, then I can still pass MyBean.class and it works. How does jackson do that?
public class MyBean {
String str;
List<String> strList;
}
You are passing MyBean.class as the second argument to readValue() and Jackson can get the type from this through reflection. I'd guess Jackson does something like this :
MyBean.class.getDeclaredField("strList").getGenericType();
which will result in a type of java.util.List<java.lang.String>.
Note that you have a non generic class MyBean containing a List<String>. If you had for instance:
class MyGenBean<T> {
List<T> list;
}
then
MyGenBean.class.getDeclaredField("list").getGenericType();
would return java.util.List<T> and you would need a TypeReference.
I have a generic response wrapper class:
public class Response <T> {
T response;
}
and unrelated classes to be wrapped:
public class ServiceResponse {
String someField;
}
When I make a service request, I get a JSON response that looks something like:
{ "code":200, "response":{"someField":"some text"} }
Now, all my service responses have the same outer wrapper, i.e., they all have:
{ "code":200, "timestamp":"....", "response":... }
But the actual format/type of the response field is different for each service request. When I deserialize the response, I need to know the type of the response field so I can create the appropriate instance, if the deserialization was done within Response, I could use:
response = new T(jsonParser);
However, I'm doing all of this from within a library that is driven by reflection, so I normally deserialize the whole tree with code like:
wrapper = deserializer.parseObject(Response<ServiceResponse>.class)
but, at this point my parseObject method can't correctly determine the type of T.
I can use something like:
Response<ServiceResponse> response = new Response<>();
Field field = response.getClass().getDeclaredField("response");
Type type = field.getGenericType();
which then tells me that response is of type T but what I actually need is ServiceResponse
Per this SO question I tried casting as ParameterizedType but that would actually seem to apply to a field of type Response<ServiceResponse> and not the actual field within (and it fails because type can't be cast as ParameterizedType)
Is there any way to determine (at run time) the raw type of response?
Eventually, I may wind up having to create an annotation providing more details about how to deserialize the field, probably by providing a function to do it, but would prefer a more transparent approach.
Another possibility might be to actually assign a void instance of T to response at initialization time and then I could grab the actual type from that...
Check out this post:
http://mydailyjava.blogspot.com/2013/06/advanced-java-generics-retreiving.html
It's actually exactly what you're looking for.
According to this, you'll just need to extend your Response class and then query the generic type of its super.
Is there any chance to assign to class reference the parameterized type eg.
Class<Set> c1= Set.class; //OK
Class<Set<Integer>> c2 = Set<Integer>.class; //Makes error
Using .class literal with a class name, or invoking getClass() method on an object returns the Class instance, and for any class there is one and only one Class instance associated with it.
Same holds true for a generic type. A class List<T> has only a single class instance, which is List.class. There won't be different class types for different type parameters. This is analogous to how C++ implements generics, where each generic type instantiation will have a separate Class instance. So in Java, you can't do Set<Integer>.class. Java doesn't allow that because it doesn't make sense, and might give wrong intentions about number of Class instances.
However, if you want a Class<Set<Integer>>, you can achieve that will a bit of type casting (which will be safe), as shown below:
Class<Set<Integer>> clazz = (Class<Set<Integer>>)(Class<?>) Set.class;
This will work perfectly fine.
You can't do it this way, because the type-parameter information is gone at Runtime and the .class statement is actually evaluated then.
You can only do:
Set<Integer> someSet = ..
Class<?> c2 = someSet.class;
new ParameterizedTypeReference<List<ClassName>>() {} Should work.
Example is giving below for exchange method Rest Template class exchange function
Funcation Requirement
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, #Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) throws RestClientException
Implementation
restTemplate.exchange(new URI("Http://{host}:{port}/{url}"), HttpMethod.POST, customObject, new ParameterizedTypeReference<List<ResponseClassName>>() {});
I'm using Google's JSON library called Gson in one of my project.
I have a code for converting JSON String into object using GSON. I have following method to do that:
public static <T> ApiResponse<T> fromJson(String json)
{
return new Gson().fromJson(json, new TypeToken<ApiResponse<T>>() {}.getType());
}
And it seems to work fine when I do something like that:
ApiResponse<List<JobModel>> response = ApiResponse.fromJson(new String(bytes));
OR
ApiResponse<Double> response = ApiResponse.fromJson(new String(bytes));
But when I try do this:
ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes));
Where JobModel is my own class I get the following error:
com.google.gson.internal.LinkedTreeMap cannot be cast to com.pcf.api.model.JobModel
So then I went and implemented another method in ApiResponse:
public static <T> ApiResponse<T> fromJson(String json, TypeToken<ApiResponse<T>> token)
{
return new Gson().fromJson(json, token.getType());
}
And this time call it using function above:
ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes), new TypeToken<ApiResponse<JobModel>>() {});
It seems to work fine.
I just can't get my head around this as two functions do exactly same thing. The only difference is that in first it purely relies on Java's generics where in second one I pass TypeToken as a parameter.
Can anyone explain me why is that happening and is there any way to fix it ?
A TypeToken is kind of a hack with generics. It depends on subclassing the type, either with an anonymous or normal class, and using Class#getGenericSuperclass() which states
If the superclass is a parameterized type, the Type object returned
must accurately reflect the actual type parameters used in the source
code.
In other words, in an anonymous class declaration like this
new TypeToken<ApiResponse<T>>() {}.getType())
the superclass is TypeToken<ApiResponse<T>>. It's equivalent to
class Subclass extends TypeToken<ApiResponse<T>>
assuming T was in scope. So when you call Class#getGenericSuperclass(), it will return a ParameterizedType that knows about ApiReponse<T> since that is the actual type parameters used in the source code.
When you call your original function with any of
ApiResponse<List<JobModel>> response = ApiResponse.fromJson(new String(bytes));
ApiResponse<Double> response = ApiResponse.fromJson(new String(bytes));
ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes));
although the compiler will infer and bind the corresponding type as a type argument to the method invocation, the internals of the method will pass the same TypeToken object with ApiResponse<T>. Since Gson doesn't know what T is, it will use a default that depends on what it sees in the JSON. If it sees an object, it will use a LinkedTreeMap. If it sees a numeric primitive, it will use the double. Etc.
In the case where you pass a TypeToken,
ApiResponse.fromJson(new String(bytes), new TypeToken<ApiResponse<JobModel>>() {});
it's equivalent to
class Subclass extends TypeToken<ApiResponse<JobModel>>
In other words, Class#getGenericSuperclass() will return a ParameterizedType that has ApiResponse<JobModel>. Gson can extract the JobModel and use it as a hint for deserializing the JSON.
Can anyone explain me why is that happening and is there any way to
fix it ?
There's nothing really to fix. That's just how it works.
Additional reading:
is it possible to use Gson.fromJson() to get ArrayList<ArrayList<String>>?
Gson TypeToken with dynamic ArrayList item type
how does the method infer the type of <T>
Generics work at compile-time,due to lack of reified Generics in Java (it isn't possible to do a T t = new T()), Gson itself is forced to use the TypeToken approach, as you see. Otherwise Gson would have done it in a much more elegant manner.