I'm using a method that takes a Class<T> as a parameter.
The class I want to pass as a parameter also uses T. It is declared as public class MyObject<T> and has a member declared as public T mMyVar; I then have 2 classes I sometimes use for mMyVar called MyVarObject1 and MyVarObject2.
Example:
private class MyObject<T> {
public T mMyVar;
}
private class MyVarObject1 {
// some variables
}
private class MyVarObject2 {
// some variables
}
Specifically, the method I'm invoking is the JacksonUtil method fromJsonArray.
I'm not sure of the proper syntax here. JacksonUtil needs to know the exact model structure so it can parse the json, but I'm having trouble figuring out the proper syntax for this line:
MyObject<MyVarObject1> result = JacksonUtil.fromJsonArray(jsonStr, MyObject<MyVarObject1>.class);
What I have there doesn't work. My IDE selects the second parameter and says, "Cannot select from parameterized type."
I had a same problem while using with retrofit, This is my solution -
public class ResponseDS<T> {
public int s;
public String e;
public T d;
}
And if you need array of object then,
public class ResponseDSs<T> {
public int s;
public String e;
public T[] d;
}
And below is how I am using it for Retrofit -
Call<ResponseDS<UserDS>> userModelCall = ZivaUtils.getRetrofit().getUser();
I think you have the same problem, hope my solution will help you :)
I do TypedToken from Gson to parse custom objects, I think you can find something similar to use with Jackson, i will edit my answer if i find something later.
You may use TypeToken to load the json string into a custom object.
Gson gson = new Gson();
//This is an example, you probably get this from your server as Json String
MyObject<MyObject1> user = new MyObject<MyObject1>();
String myObjectAsString = gson.toJson(user);
//then parse into your custom object
MyObject other = gson.fromJson(myObjectAsString, new TypeToken<MyObject<MyObject1>>(){}.getType());
Related
I am facing an issue with generics.
Here is one of my classes that uses generics:
public class TokenServerResponse<D> {
private String responseCode;
private String responseMessage;
private D responseData;
....
Here our class: TokenServerResponse is parameterized with D.
I would like to specify the type in one of our methods as follows:
protected ResponseEntity<TokenServerResponse<DigestResponseData>> digest(long globalMerchantUId, String expirydate, String pan, boolean updateExpiryDate) throws Exception {
DigestRequest digestRequest = new DigestRequest();
digestRequest.setGlobalMerchantUid(globalMerchantUId);
digestRequest.setExpiryDate(expirydate);
digestRequest.setPan(pan);
digestRequest.setUpdateExpiryDate(updateExpiryDate);
return restTemplate.postForEntity("/digest", digestRequest, TokenServerResponse<DigestResponseData>.class);
}
However, I get the following compiler error: cannot select from parameterized type.
How can I use the type parameter D? I have also tried casting to no avail. What am I getting wrong?
Here is how the digest method is called:
ResponseEntity<TokenServerResponse<DigestResponseData>> digestResponseEntity = digest(823, "1505", pan, true);
Here :
return restTemplate.postForEntity("/digest", digestRequest, TokenServerResponse<DigestResponseData>.class);
If your method expects to have a class value as last argument, you can only provide a class for it. Providing a class with generic type is not possible.
Casting is unavoidable but if you change your TokenServerResponse class to use also inheritance.
public abstract class TokenServerResponse<T> {
private String responseCode;
private String responseMessage;
private T responseData;
public T getResponseData() {
return responseData;
}
}
public class TokenServerResponseDigestResponseData extends TokenServerResponse<DigestResponseData> {
}
Now you can use TokenServerResponseDigestResponseData class here :
return restTemplate.postForEntity("/digest", digestRequest, TokenServerResponseDigestResponseData.class);
And when you do :
TokenServerResponseDigestResponseData instance = ...
DigestResponseData data = instance.getResponseData();
you don't need any cast.
Of course this solution is interesting if you have not dozen of classes to make them inherited from the TokenServerResponse class and you would like to work with specific types in client code.
In your case, DigestResponseData is required to make your processing since your generic type doesn't rely on a specific type but on Object type, so you should cast at a time in this way : TokenServerResponse to TokenServerResponse<DigestResponseData>.
With the proposed solution, it is not required any longer.
After multiple researches on Google and Stack Overflow, i haven't found a similar case to mine.
I need to use Gson library to convert a Java object to Json. The fact is that this object contains a field with a custom generic type, as follow :
SendData.java :
public class SendData {
private SendDataRequestObject<?> sendData;
// Constructor + Getters and Setters
}
Here is the class definition of SendDataRequestObject :
public class SendDataRequestObject<T> {
private String actionType;
private T parameters;
private CustomClass customClass;
//Constructor + Getters and Setters
}
And finally, the class definition of MyRequest which may be injected in SendDataRequestObject as the T parameter
public class MyRequest {
private Map<Integer, String> myMap;
private String myString1;
private String myString2;
//Constructor + Getters and Setters
}
Actually, I'm able to parse SendDataRequestObject with Gson library as follow :
SendDataRequestObject<MyRequest> requestObject = new SendDataRequestObject<MyRequest>();
//...
//Initializing and adding fields to requestObject
//...
Type token = new TypeToken<SendDataRequestObject<MyRequest>>(){}.getType();
System.out.println(new GsonBuilder().create().toJson(requestObject, token));
The output is properly set and every fields, even the generic one, are included into the final json string :
{"actionType":"verify","parameters":{"myMap":{"15789":"hreher-489hre-gdsf","13057":"rtyuiop-4g8ezg","16741":"gfd456-uiop789"},"myString1":"myStringValue1","myString2":"myStringValue2"},"customClas":{"attr1":"value1","attr2":"value2"}}
But what I need is to parse SendData class, not SendDataRequestObject class. When I try to convert this class into json string, I obtain this output :
{"sendData":{"actionType":"verify","parameters":{},"customClass":{"attr1":"value1","attr2":"value2"}}}
So, we can see that parameters field of SendDataRequestObject is not converted to Json, probably because this is a generic class.
If anybody has an idea of how to do it, I would be very grateful !
You can't do this without somehow knowing the type T at compile time in some manner due to Java's type erasure.
One option for this is the JSON can contain some information specifying the type, e.g.
{
"sendDataType": "MyRequest",
"sendData": {
...
}
}
If you then make SendData generic e.g.
SendData<T> {
private SendDataRequestObject<T> sendData;
}
you can then parse the JSON once to find out the sendDataType:
SendData<?> genericSendData = new GsonBuilder().create().toJson(requestObject, new TypeToken<SendData<?>>(){});
String sendDataType = genericSendData.sendDataType;
and use that to create a TypeToken of the right type:
switch(sendDataType) {
case "MyRequest":
return new TypeToken<MyRequest>(){};
}
And then parse the JSON again specifying the generic type now that you know it:
SendData<?> myRequestSendData = new GsonBuilder().create().toJson(requestObject, typeToken);
This works because our switch statement knows the possible types at compile time and can create TypeTokens for them.
I have a wrapper json object for example
{
"id": 23,
"name": "teset",
"type": "person",
"_data": {
"address": 23432
}
}
my java object would look like this
public class Wrapper<D>{
private Integer id;
private String type;
#JsonProperty("_data")
private D data;
...
}
i cannot find a way to have the object mapper do this
Wrapper<Person> wrapped = objectMapper.readValue(jsonStream,Wrapper.class);
is this not supported, i haven't been able to find much information about generics in Jackson.
There are a few problems with your code:
The main issue is that you are not specifying the desired parametrized type of Wrapper in your readValue invocation. You can fix this by using (simplified form): Wrapper<Person> wrapped = om.readValue(json, new TypeReference<Wrapper<Person>>() {});
Also, your JSON features a name property that is not apparently present in your Wrapper class. You either have it and haven't posted it, or you can configure your ObjectMapper to ignore unknown properties: objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Here's an example:
public static class Wrapper<D> {
// making fields public for simplicity,
// use public getters and private fields of course
public Integer id;
public String type;
#JsonProperty("_data")
public D data;
}
public static class Person {
// adding address field as a public int,
// same as above, encapsulate properly in real life
public int address;
}
Then, in a main method somewhere...
ObjectMapper om = new ObjectMapper();
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// your example JSON
String json = "{\"id\":23,\"name\":\"test\",\"type\":\"person\",\"_data\":"
+ "{\"address\":23432}}";
Wrapper<Person> wrapped = om.readValue(
json, new TypeReference<Wrapper<Person>>() {}
);
// printing class/hashCode of the resolved generic type
System.out.println(wrapped.data);
// casting as Person and printing actual property
System.out.println(((Person)wrapped.data).address);
Output (similar to...)
test.Main$Person#dfd3711
23432
Explanation for TypeReference, from the docs:
This generic abstract class is used for obtaining full generics type
information by sub-classing; it must be converted to ResolvedType
implementation (implemented by JavaType from "databind" bundle) to be
used. Class is based on ideas from
http://gafter.blogspot.com/2006/12/super-type-tokens.html, Additional
idea (from a suggestion made in comments of the article) is to require
bogus implementation of Comparable (any such generic interface would
do, as long as it forces a method with generic type to be
implemented). to ensure that a Type argument is indeed given.
Usage is by sub-classing: here is one way to instantiate reference to
generic type List:
TypeReference ref = new TypeReference<List<Integer>>() { };
which can be passed to methods that accept TypeReference, or resolved
using TypeFactory to obtain ResolvedType.
I'm using Jackson to deserialize feeds and pull specific values from it. I'm trying to do this without know what feeds will be selected at runtime. I attempted to make a generic class that contains the method for deserializing the feed, and it works oddly enough. The problem is I can't pull any information from it without explicitly stating which class I am pulling from.
public class Motor<T> {
final ObjectMapper jsonMapper = new ObjectMapper();
public Object pullJson( String url, Object blah ) throws IOException{
#SuppressWarnings("unchecked")
T mapped = jsonMapper.readValue(new URL(url), (Class<T>) blah);
return mapped;
}
//Some other code
}
public class Root{
public static String value;
public String getValue(){
return value;
}
}
public class Propeller {
public static void main(String[] args) throws IOException{
Motor<?> motor = new Motor();
List<Class<?>> classes = new ArrayList<Class<?>>();
classes.add(Root.class)
String url = "blah.com/blah.json"
Object blah = classes.get(0);
blah = motor.pullJson(url, blah);
System.out.println(blah.value);
}
}
The only way I could think to pass a class into the method without know what the class will be was to set the class parameter as an Object and then pass a generic class onto it.
If I we're to ask for Root.value, it'll give me value's value, but I won't know what class the method will act on at runtime. And trying to act on the class declared as blah will tell me value cannot be resolved. I have a feeling my thought process here is extremely flawed in trying to pass a class as an object and reading the values that way.
Using Gson, I'm trying to de-serialize a a nested, generic class. The class structure looks like the following:
Wrapper object, simplified, but normally holds other properties such as statusMessage, which are returned along with the data-field from the server:
public class Response<T> {
private List<T> data = null;
public List<T> getData() { return this.data; }
}
Simple class, the expected output from data-field above (though as an array):
public class Language {
public String alias;
public String label;
}
Usage:
Type type = new TypeToken<Response<Language>>() {}.getType();
Response<Language> response = new Gson().fromJson(json, type);
List<Language> languages = response.getData();
Language l = languages.get(0);
System.out.println(l.alias); // Error occurs here
Where the json-variable is something like this.
However, when doing this, I recieve the following exception (on line 3, last code example):
ClassCastException: com.google.gson.internal.StringMap cannot be cast to book.Language
The exception ONLY occurs when storing the data from getData() into a variable (or when used as one).
Any help would be highly appreciated.
The problem you're actually having is not directly due to Gson, it's because of how arrays and Generics play together.
You'll find that you can't actually do new T[10] in a class like yours. see: How to create a generic array in Java?
You basically have two options:
Write a custom deserializer and construct the T[] array there as shown in the SO question I linked above
Use a List<T> instead, then it will simply work. If you really need to return an array, you can always just call List.toArray() in your method.
Edited from comments below:
This is a fully working example:
public class App
{
public static void main( String[] args )
{
String json = "{\"data\": [{\"alias\": \"be\",\"label\": \"vitryska\"},{\"alias\": \"vi\",\"label\": \"vietnamesiska\"},{\"alias\": \"hu\",\"label\": \"ungerska\"},{\"alias\": \"uk\",\"label\": \"ukrainska\"}]}";
Type type = new TypeToken<Response<Language>>(){}.getType();
Response<Language> resp = new Gson().fromJson(json, type);
Language l = resp.getData().get(0);
System.out.println(l.alias);
}
}
class Response<T> {
private List<T> data = null;
public List<T> getData() { return this.data; }
}
class Language {
public String alias;
public String label;
}
Output:
be