How to convert List<?> to List<Object> in java? - java

How to convert List<?> to List in java?
For example I have this class
#Data
public class Example {
private List<?> data;
}
and I used in this function
#PostMapping("/getResult")
#ResponseBody
public Result getResult(#RequestBody String json) {
Gson gson = new Gson();
Example xmpl = gson.fromJson(json, Example.class);
List<MyObject> source = (List<MyObject>)xmpl.getData(); //==> error
// get Result
return result;
}
It will give this error
class com.google.gson.internal.LinkedTreeMap cannot be cast to class com.myproject.MyObject
EDITED:
The real problem is not from converting ? to object, but from converting LinkedTreeMap to the object
WORKAROUND :
String jsonData = gson.toJson(xmpl.getData());
MyObjectBean[] objs = gson.fromJson(jsonData,MyObjectBean[].class);

You could go with two solutions, to start with:
You can change the Generic type, this way You don't say data is any collection, but it's a collection of type <T>. Now You can create classes with given type anywhere You need it.
Generic value <?> means in general that you don't care what is inside, and probably You won't read it anyway. When You are interested only if collection is null or what it's size.
When You need to do something with it, then use Generic types.
Example:
public class Example<T> {
private List<T> data;
}
Now inside of your controller, create a private class, to deserialize your payload.
static class MyObjectExample extends Example<MyObject>{
}
Now you can use it do decode JSON:
MyObjectExample xmpl = gson.fromJson(json, MyObjectExample.class);
List<MyObject> source = xmpl.getData();
Now if your code can be serialized to MyObject it will work.
Spring supports deserialization also.
If you have a #RestController annotation added to your Controller class
Example:
#PostMapping("/getResult")
public Result getResult(#RequestBody MyObjectExample xmpl) {
// get Result
return result;
}
Or you can add
consumes = MediaType.APPLICATION_JSON_VALUE
to your REST method.
Try using Spring to convert your value for you.
You can find more
GetMapping and PostMapping
tutotrial

The real issue is not when converting ? to MyObject, but the LinkedTreeMap to MyObject, from
this explanation
by #harsh
so I did this workaround
String jsonData = gson.toJson(xmpl.getData());
MyObjectBean[] objs = gson.fromJson(jsonData,MyObjectBean[].class);

Related

Gson-> Json to deserialize List of custom objects

I am trying to serialize and de-serialize an ArrayList of Java POJOs using Gson on Json objects
I have an object MyClass as
public class MyClass{
private int type
private int pos;
private Object value;
}
I have an ArrayList of these objects, which I serialize as
List<MyClass> values= null;
String json = new Gson().toJson(retValues);
The json string is
[{"type":4,"pos":1,"value":15}]
I try to deserialize it as
Type myType = new TypeToken<ArrayList<MyClass>>() {}.getType();
List<MyClass> test=new Gson().fromJson(json, myType);
I get an error
The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#1141ddf failed to deserialized json object [{"type":4,"pos":1,"value":18}] given the type java.util.ArrayList<abc.MyClass>
Any input much appreciated!
I figured it out. I added 2 things, I don't know which one made it work.
- I added a no-arguments constructor to MyClass
- I made MyClass implement serializable.
And it works!
Thanks for your help.

How to get the Generic type for the Jackson ObjectMapper

Java erases normally the Generics data on compilation, but there is a possibility to get that information (the Jackson ObjectMapper does that pretty well).
My Problem: I have a Class with a property of List:
public class User {
public List<Long> listProp;//it is public only to keep the example simple
}
How can I get the correct TypeReference (or JavaType ?) so that I can map programatically a JSON String to the correct List Type, having the instance of the Class class (User.class) and the property name (listProp)? What I mean is this:
TypeReference typeReference = ...;//how to get the typeReference?
List<Long> correctList = om.readValue(jsonEntry.getValue(), typeReference);//this should return a List<Long> and not eg. a List<Integer>
Have you tried the mappers constructType method?
Type genericType = User.class.getField("listProp").getGenericType();
List<Long> correctList = om.readValue(jsonEntry.getValue(), om.constructType(genericType));
jackson use TypeReference to construct generic type
TypeReference typeReference =new TypeReference<List<Long>>(){}
jackson use JavaType to construct generic type
JavaType jt = om.getTypeFactory().constructArrayType(Long.class);
jackson support three types
Class
JavaType
TypeReference
i like use JavaType, it is more clear for generic type, for normal object use Class
Perhaps a less exotic approach to deserializing a generic type is to wrap it inside a concrete type:
class ListLongWrapper extends ArrayList<Long> {} // package scope
... or ...
static class ListLongWrapper extends ArrayList<Long> {} // class scope
then
String jsonStr = objMapper.writeValueAsString(user1.listProp); // serialize
user2.listProp = objMapper.readValue(jsonStr,ListLongWrapper.class); // deserialize
Notice that extends requires a class type (here I used ArrayList) instead of the interface List.
This suggests an even more direct approach for the given example -- User is already a wrapper (and listProp is public):
public class User {
public List<Long> listProp;
}
then
String jsonStr = objMapper.writeValueAsString(user1); // serialize
var user2 = objMapper.readValue(jsonStr,User.class); // deserialize
In this case, you can use the interface List as-is as a field type within the wrapping class, but this means you have no control over the concrete type that Jackson will use.

java jackson parse object containing a generic type object

i have the following problem.
I have to parse a json request into an object that contains a generic type field.
EDIT
i have made some tests using a regular class type (so i make it work before i replace it with generic). Now parsing for a single element works great.
The issue is when i need to parse out a list object out of that class.
So i have to inform jackson somehow that my T is of type list instead of just AlbumModel.
Here is what i have tried.
#Override
public ListResponseModel<AlbumModel> parse(String responseBody) throws Exception {
JavaType type = mapper.getTypeFactory().constructParametricType(ResponseModel.class,
AlbumModel.class);
return mapper.readValue(responseBody,
mapper.getTypeFactory().constructParametricType(ResponseModel.class, type));
}
But the code above doesn't work. what is the solution for something like this?
my generic type in the ListResponseModel is defined like: List<T> data
succeeded like:
public class BaseResponseModel<T> {
#JsonProperty("data")
private T data;
#JsonProperty("paginations")
private PaginationModel pagination;
}
so far i have the following code but it always parses into a Hash.
public class ResponseParser extends BaseJacksonMapperResponseParser<ResponseModel<AlbumModel>> {
public static final String TAG = ResponseParser.class.getSimpleName();
#Override
public ResponseModel<AlbumModel> parse(String responseBody) throws Exception {
return mapper.readValue(responseBody,
mapper.getTypeFactory().constructParametricType(ResponseModel.class, AlbumModel.class));
}
}
public abstract class BaseJacksonMapperResponseParser<T> implements HttpResponseParser<T> {
public static final String TAG = BaseJacksonMapperResponseParser.class.getSimpleName();
public static ObjectMapper mapper = new ObjectMapper();
static {
mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
}
}
I agree with eugen's answer but just wanted to expand on it a bit. The first step is to refactor your parse method so it takes a second argument. Instead of allocating the type reference in your method, you require the caller to pass in a TypeReference instance.
public BaseResponseModel<T> parse(String responseBody, TypeReference<T> ref) throws Exception {
return mapper.readValue(responseBody, ref);
}
Unfortunately your snippet does not show the code which calls parse - so I'll make something up:
BaseResponseParser<Collection<Person>> parser = new BaseResponseParser<Collection<Person>>();
BaseResponseModel<Collection<Person>> result = parser.parse(jsonText, new TypeReference<Collection<Person>>(){});
Notice that when the TypeReference instance is compiled in this case, it a type reference to the real concrete class that we expect.
You could do the same thing passing in a Class at runtime, however TypeReference is a bit more powerful because it even works when type T is a generic collection. There is some magic in the TypeReference implementation that allows it to hold onto type information that would normally be erased.
[update]
Updated to use Collection<Person>. Note - as far as I know as List<Whatever> should work also, but I double checked a project where I was using jackson to deserialize collections. Base class Collection definitely worked so I stayed with that.
Your type T will be "erased" at runtime, so Jackson does not know what is the real type of T and deserializes it to a Map. You need a second parameter to your parse method that will be Class<T> clazz or TypeReference<T> or java.lang.reflect.Type.
EDIT
Small explanation on the magic of TypeReference. When you do new XX() {} you are creating a anonymous class, so if it is a class with typevariables (parameterized if you prefer), new X<List<Y>>() {}, you will be able to retrieve List<Y> as a java Type at runtime. It is very similar as if you had done :
abstract class MyGenericClass<T> {}
class MySpecializedClass extends MyGenericClass<List<Y>> {}
Since you're using Jackson you probably need to create a custom JsonDeserializer or JsonSerializer depending on whether you're handing the response or request. I've done this with Dates because on my response I want a standard view. I'm not 100% positive it will work with a generic field though. Here is an example of what I'm doing:
public class DateSerializer extends JsonSerializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZ");
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
String dateString = dateFormat.format(value);
jgen.writeString(dateString);
}
}
Then I just add it to my class like so:
#JsonSerialize(using = DateSerializer.class)
public Date getModifiedDate() {
return modifiedDate;
}

How to create object that can be added to a List<T> array?

I am trying to create a JSONMessage class that can parse a json string and add the objects contained in the message to the List<T> t.
The object contained in the message implements the interface JSONSerialisation and implements the two methods toJSON() and fromJSON().
The code below does not work because I cannot instantiate the Type T and I get an error on the row t2.fromJSON... (as t2 has not been initialized).
I am not sure if my approach here is correct and if what I try to is achievable (create a generic JSONMessage) that I can use to encode/parse different type of objects. If that approach is not possible, I would appreciate hints of how I could achieve a similar result.
Regards
Interface
public interface JSONSerialisation {
public JSONObject toJSON();
public void fromJSON(JSONObject jsonObject);
}
Class
public class JSONMessage<T extends JSONSerialisation> {
private List<T> t;
public JSONMessage(String json) {
parseJSONMessage(json);
}
public void parseJSONMessage(String json) {
try {
this.t = new ArrayList<T>();
JSONObject jsonObject;
JSONArray lineItems;
jsonObject = new JSONObject(json);
this.messageHeader = new MessageHeader(jsonObject.getJSONObject("Header"));
lineItems = jsonObject.getJSONArray("Data");
int size = lineItems.length();
for (int i = 0; i < size; i++) {
T t2;
t2.fromJSON(lineItems.getJSONObject(i));
t.add(t2);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Looks like you are trying to replace the functionality available from most JSON deserialization libraries. You will notice that most of them require a parametized class, as a solution to the very problem you are facing. It's not the most elegant solution, but it will work. I would drop the generic parameter from the deserialization class, and the stateful variables as well:
public class JSONMessage {
public static <T extends JSONSerialisation> Collection<T> parseJSONMessage(Class<T> clazz, String json) {
try {
final JSONObject jsonObject = new JSONObject(json);
final JSONArray lineItems = jsonObject.getJSONArray("Data");
Collection<T> results = new ArrayList<T>(lineItems.size());
for (final JSONElement elem: lineItems) {
T result = clazz.newInstance();
result.fromJSON(elem);
results.add(result);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public JSONMessage() {
super();
}
}
The code is obviously missing some error handling, and I made up a bogus JSONElement type in the enhanced for loop, but you get the general idea. Good luck.
Try this:
this.t = new ArrayList<JSONSerialisation>();
If you eliminate all references to this inside that method you can make it static. Just create a List in scope and return it.
I don't see what generics are buying you here. How likely is it that you're going to have different implementations of that JSON interface?
Have you considered including the object class in your JSON message?
What about a factory class that implements fromJSON(JSONObject) so you can create an instance of the object without first having an instance of the object? The factory could use information about the context within which the JSON was generated to determine the appropriate Java classes. For example, if you've just invoked web-service X you might know the response will either be an piece of data of type Y or an error message of type Z.
In your code above, what if T has a sub-class? How would you distinguish between them?
Ignoring JSON, I thought I'd also answer the part of your question on generics: if you know you have a List, what is T? Java generics work by type-erasure, which means during compilation a lot of the generic type information is erased. However, if you are serializing a POJO into JSON there is good news. The generic type information is retained for classes, fields and methods, so if you had a class like this:
class Example extends ArrayList<String> {
private Set<Integer> someIDs;
public Map<String,Long> getLongMap() {
...
}
}
Then it is possible to use reflection to discover that the class implements List where T is String, that the someIDs field is an instance of Set where T is Integer and the getLongMap method returns an instance of Map where K is String and V is Long. However working your way through the reflection API to get to this information can get quite involved. My code to just identify Collection, Iterator and Enumeration types runs to over 100 lines. I would not recommend trying to do it if you have alternatives.

GSON: Typing JSON data to Java class where class name is stored in string

I'm using Google's GSON Library to convert JSON data to Java objects. The issue is that class name of the Java objet is being passed in the JSON data as well, so it is available only as a string. I'm not too familiar with Java so I don't understand how to declare an object when the class name is stored as a string.
Also, the same is the case for the method that I will be calling after the data is initialized.
Here's the relevant code so far:
request = gson.fromJson( rawData.toString(), JSONRequest.class );
String method = request.getMethod();
String data = request.getData();
String dataClass = request.getDataClass();
// convert data into an object of dataClass and execute method by passing dataObject
dataClass dataObject = gson.fromJson( data, dataClass.class );
result = method( dataObject );
This seems like a very crude way to accomplish the data to object conversion. Is there a better way? If not, how can I fix the code?
With the example code in the original question, dataClass.class will always be String.class, which I doubt is the correct target type to deserialize to.
I think the following is what was intended to be demonstrated. (I'm not sure what implementation of JSONRequest was used in the original question. I'll just make one up.)
If the target type is really not known, and it's not a sub-type of some known parent type, then reflection would be necessary to invoke the method. (This assumes that the target method does not require any parameters.)
import java.io.FileReader;
import java.lang.reflect.Method;
import com.google.gson.Gson;
public class Foo
{
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
// input JSON:
// {
// "dataClass":"com.stackoverflow.q6647866.MyObject",
// "method":"myMethod",
// "data":"{\"name\":\"fubar\"}"
// }
JSONRequest request = gson.fromJson(new FileReader("input.json"), JSONRequest.class);
Class targetClass = Class.forName(request.dataClass);
System.out.println("targetClass: " + targetClass);
Object dataObject = gson.fromJson(request.data, targetClass);
Method method = targetClass.getMethod(request.method);
method.invoke(dataObject);
}
}
class JSONRequest
{
String method;
String data;
String dataClass;
}
class MyObject
{
String name;
public void myMethod()
{
System.out.println("running my method...");
}
}
This seems like a very crude way to accomplish the data to object conversion. Is there a better way?
That's about as good as it gets, if the target data type and method to invoke are both completely unknown and only provided in the incoming JSON.

Categories