Since few days ago I started to work on a webservice project. This project is using Jackson to marshalling and unmarshalling JSON objects. So my question is:
Why always I have to put the {} when I am creating an instance of TypeReference? I know the constructor is protected, but why is protected? I think that it's like a hack to make visible the constructor creating an implementation of the constructor since TypeReference is abstract and you can do it. But what is the point of this?
String jsonString = "{\" firstName\":\"John\",\"lastName\":\"Chen\"}";
ObjectMapper objectMapper = new ObjectMapper();
// properties will store name and value pairs read from jsonString
Map<String, String> properties = objectMapper.readvalue(
jsonString, new TypeReference<Map<String, String>>()
{ //
});
TL;DR
Via subclassing it is possible for TypeReference to extract the actual generic type parameter. E.g:
TypeReference<String> ref = new TypeReference<String>(){};
System.out.println(ref.getType());
Prints:
class java.lang.String
This can be useful when you can't use normal classes. E.g when this doesn't work:
// doesn't work
Type type = ArrayList<String>.class;
You still can get that class by using a TypeReference:
// will yield Class<ArrayList<String>>>
Type type = new TypeReference<ArrayList<String>>(){}.getType();
Detailed
When looking at the source code of TypeReference (using Jackson 2.8.5) you can see that the constructor body contains the following lines:
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof Class<?>) { // sanity check, should never happen
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
}
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
The interesting lines are the first and last. Let's take a closer look at the first line:
Type superClass = getClass().getGenericSuperclass();
For example when you're creating a subclass, by using an anonymous class:
TypeReference<SomeStype> ref = new TypeReference<SomeType>(){};
Then getClass returns the current Class object (an anonymous class), and getGenericSuperclass() will return the Class object from the class the current implementation extends from, in our case, superClass will equal Class<TypeReference<?>>.
Now when looking at the last line from the constructor body:
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
As we know that the superClass is the Class object for TypeReference<?> we know that it has a generic parameter. Hence the cast to ParameterizedType. This specified Type has the method getActualyTypeArguments() which returns an array of all generic parameters specified by that class. In our case it's just 1. So [0] will yield the first element. In the example we will get the actually specified type parameter SomeType.
Related
I am trying to serialize instances of a generic class while preserving the generic types, so I will be able to deserialize it later without having to specify the generic type manually.
I understand that the usual way of deserializing generic objects is by using type references or JavaType objects like this:
ObjectMapper om = new ObjectMapper();
ObjectReader or = om.reader();
ObjectWriter ow = om.writer();
String json = "[1, 2, 3]"
JavaType listType = or.getTypeFactory()
.constructParametricType(List.class, Integer.class);
List<Integer> integers = or.forType(listType).readValue(json)
But I do not know the generic type (Integer in this case) beforehand, therefore I can't do this.
I also understand that, because of erasure, I'll have to include the type information within the serialized JSON in some way or another. This can be done with the #JsonTypeInfo annotation:
class Pojo<T> {
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public T value;
}
However, this quickly becomes bloated if the type T is used in various other places. Consider the following example:
class Pojo<T> {
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public T value;
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public List<T> otherStuff;
// constructor
}
// ...
Pojo<BigDecimal> pojo = new Pojo<>(
BigDecimal.valueOf(42),
Lists.newArrayList(BigDecimal.valueOf(14), BigDecimal.valueOf(23))
);
final String json = ow.writeValueAsString(pojo);
System.out.println(json);
produces the following result:
{
"value": ["java.math.BigDecimal", 42],
"otherStuff":[
["java.math.BigDecimal", 14],
["java.math.BigDecimal", 23]
]
}
which repeats the type, BigDecimal in this case, for every single object. This is unnecessary because the type is the same for all occurances of T anyway (except in some polymorphic cases I suppose).
If the #JsonTypeInfo annotation is omitted for otherStuff, Jackson is unable to defer the type of the contents of otherStuff from the type of value. In this example it will deserialize otherStuff as List<Integer>, even though value is of type BigDecimal.
How can I serialize instances of a generic class, so that I can safely deserialize them later and retain the generic argument?
The type information indeed needs to be included in the serialized json string, but only once. The easiest way I know of is to write a custom creator method using the #JsonCreator annotation to perform the deserialization in two steps:
Let Jackson deserialize all non-generic fields, and one generic field that includes the type information. Capture the other generic fields as raw JsonNode, so they can be manually deserialized.
Take that type information at runtime to deserialize the remaining fields.
For the example above this would look like this (exception handling code omitted):
class Pojo<T> {
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public T value;
public List<T> otherStuff;
// constructor
#JsonCreator
public static <T> Pojo<T> jsonCreator(
#JsonProperty("value") T value,
#JsonProperty("otherStuff") JsonNode otherStuffRaw) {
JavaType listType = or.getTypeFactory()
.constructParametricType(List.class, value.getClass());
return new Pojo<T>(
value,
or.forType(listType).readValue(otherStuffRaw)
);
}
}
This, however, allows value to be a subclass of T, which might produce unexpected results. If this is a concern, another approach might be using Class<T> to retain the exact type of T. This would also shield against value possibly being null.
I have an HTTP request handler that returns deserialized JSON an Object, which is derived from an abstract Request and then cast to the correct class. I'm having some issues making this handler return a List though.
public class ListMyResource extends AbstractRequest
{
public boolean isCollection;
public ListTransactions()
{
this.isCollection = true;
}
public final RequestMethod REQUEST_METHOD = RequestMethod.GET;
public final String URL_METHOD = "";
public Class<ArrayList<MyResource>> getResourceClass()
{
return new ArrayList<MyResource>().getClass();
}
}
public Object getDeserializedResponse()
{
Response response = new Response(this.request, true);
try
{
if (this.request.isCollection())
{
List<this.request.getResourceClass()> list = new ArrayList<this.request.getResourceClass()>();
listType = new TypeToken<ArrayList<AbstractObject>>() { }.getType();
response.setData(deserialize.fromJson(this.getResponse(), listType));
}
else
{
response.setData(deserialize.fromJson(this.getResponse(), this.request.getClazz()));
}
}
catch (JsonSyntaxException jse)
{
MyLibrary.LOG.error("Could not parse JSON", jse);
response.setRequestWasSuccessfull(false);
}
return response;
}
The above code works fine if isCollection() returns false and the method only has to come up with a single deserialized object, but it doesn't work for collections as I can't put the result from getResourceClass() in the <> from the List. That results in Identifier expected. How can I approach this so I achieve the desired result?
ArrayList<this.request.getResourceClass()>
This cannot work. Java type parameters must be compile-time "constants" (I mean, something known to the compiler). So,
List<String> strings
is a valid syntax for the type parameters, but
List<some.runtime.expression.here()>
is not. Next thing, I would strongly recommend you not to use Class<?> to pass data type information. Note that Class<?> holds information about a real type in your system. Consider you want to have a list of strings, a list of integers and a list of booleans types. You can't do ArrayList<String>.class -- it's an illegal expression in Java, because actual type parameters are not a part of class information. Can you subclass ArrayList with something like extends ArrayList<String> and so on? You can but you shouldn't. What if the class to subclass is final or you're going to use LinkedList? Some sort of code bloat, isn't it?
Class is java.lang.reflect.Type, and Gson requires an instance of the latter to be passed to the fromJson() method. What if you construct it yourself? Gson provides a convenient mechanism to construct java.lang.reflect.Type and java.lang.reflect.ParameterizedType (the latter is used for collections very intensively in Gson by the way, see more for Gson TypeTokens). The are two ways:
TypeToken.getParameterized(rawType, typeParameter1, typeParameter2)
For example, TypeToken.getParameterized(List.class, String.class).getType() will return a ParameterizedType instance as if it you could write List<String>.class. This is a truly dynamic approach, and you can pass some runtime execution results to the getParameterized() method.
TypeToken subclassing
The trick here is that TypeToken is an abstract class and it can be parameterized, letting Gson analyze a usually anonymous class for the actual parameters at runtime and let the getType() method return a compile-time composed type: new TypeToken<List<String>>(){}.getType() (subclasses can store information about their super classes parameterization, and Gson uses it at runtime). Note that getRawType returns List.class only - this is all just how Java generics are implemented since Class cannot store actual type parameters (read more for Java generics and erasure).
Having this vague explanation in mind, just refactor your getDeserializedResponse() method making a request return java.lang.reflect.Type rather than Class<?>. I even think that you can remove isCollection from that method and just let Gson do it all itself: Gson can distinguish between List<String> and List<Integer> types.
I wanted to pass object as a parameter instead of class object as type literal. I tried many ways but did not get output.
public <T> List<Map<String,Object>> getUIElementsList(Class<T> requiredType) {
doSomeThing();
return this.fieldList;
}
If i'm running above code that will accept following values as parameter passing. If I have a FormBean class then
FormBean formBean = new FormBean();
formBean.setUserId(252528);
getUIElementsList(FormBean.class); //restrict this case
getUIElementsList(formBean);
I want that method can only accept already created intance object. I can not even use newInstance() method to create another object because i required old object instance values also.
Any suggestions?
The Class<T> itself represents some instance, too - this is an instance of the type Class, parameterized by the type T.
I think the best you can do is to add a check if the provided type is instance of Class and if yes, throw an IllegalArgumentException:
public <T> List<Map<String,Object>> getUIElementsList(T value) {
if (value instanceof Class) {
throw new IllegalArgumentException("Class instances are not supported");
}
..
}
When defining type-parameters, you're only allowed to bound them by intersections of existing (families of) types, but not to apply negation on given types, like the thing you aim for, which is something like "all types but one".
I have some code which needs to unpick a Jackson TypeReference to find out if it is a Collection. At the moment the best I can come up with is:
// Sample type reference - in reality this is an argument to the method
final TypeReference<List<String>> typeRef = new TypeReference<List<String>>(){};
// Obtain the Java reflection type from the TypeReference
final Type type = typeRef.getType() instanceof ParameterizedType ? ((ParameterizedType)typeRef.getType()).getRawType() : typeRef.getType();
// Obtain the name of the class (or interface)
final String typeName = type.toString().replace("class ", "").replace("interface ", "");
// And find out if it is a Collection
final boolean isCollection = Collection.class.isAssignableFrom(Class.forName(typeName));
But I would hope that there is a way to do this without string manipulation. Is there a better way to go from the Java Type to the Class, or indeed to check assignability directly from either the TypeReference or the Type?
This needs to work on Android so any features added in Java 8 can't be used.
Based on your line of code,
final Type type = typeRef.getType() instanceof ParameterizedType ? ((ParameterizedType)typeRef.getType()).getRawType() : typeRef.getType();
You can safely cast it to a Class like this
final Class clazz = (Class)(typeRef.getType() instanceof ParameterizedType ? ((ParameterizedType)typeRef.getType()).getRawType() : typeRef.getType());
To add a little more explanation -
In the first scenario where ( typeRef is an instance of ParameterizedType), you are retrieving the rawType which would be a Class.
In the second scenario where (typeRef is not an instance of ParameterizedType), it would still be a regular Class because it is not Parameterized.
I'm trying to deserialize some generic type. It works nice, but I would like to swap this piece of code out to a method.
Type listType = new TypeToken<TestResult<MyObject>>(){}.getType();
TestResult<MyObject> result = new Gson().fromJson(json, listType);
How to I do this, proving information about the type?
Yes you can. There is a workaround.
You just have to explicitly pass the type and class of "MyObject" to the method.
In your case, develop a method having the following signature and body.
public <T> Object getGenericDataObjectFromJson(String json, Type t, Class<T> type) {
// Convert Json String to Java object
TestResult<T> obj = gson.fromJson(json, t);
return obj;
}
Where
String json = your json string
Type listType = your listType object passed as a parameter
Class type = MyObject.class
And the method call would become like this
GsonUtility gs = new GsonUtility();// Your gson utility class
JSONObject jsonObj = "your json string";
// Explicitly identifying the type so as to pass it to the method
// Because The type of a generics can't be determinted during runtime as #CKuk said
Type objectType = new TypeToken<DataObject<objectType>>(){}.getType();
TestResult<MyObject> resultObject= (TestResult<MyObject>) gs.getGenericDataObjectFromJson(jsonObj.toString(), objectType, MyObject.class);
You can't.
The type of a generics can't be determinted during runtime.
An easy workaround would be to add a method to MyObject to return the resulting Type (like the first line of your code sample).