Gson How to covert json to Custom Class - java

Somary for below: I need to deserilize A<B<C>> from json with Java Gson library
I have an api that response in this templete (Json):
{code:string, message:string, data:(any json type/object/array)}
In Java I define a class to read results:
public class ApiResult <DATA> {
public String code;
public String message;
public DATA data;
}
And I have another Data Class:
public class Ball {
public String color;
public String weight;
}
One of my api methods returns list of ball in top template.
I use GSON.fromJson(reader, new TypeToken<ApiResult<List<Ball>>>() {}.getType());
But Gson read data in List<com.google.gson.internal.LinkedTreeMap> not List
How can I fix this?

Finally I solve my question but not for all A<B<C>> problems
I redefine my ApiResult class to:
public class ApiResult {
public String code;
public String message;
public JsonElement data;
}
Using result = GSON.fromJson(reader, ApiResult.class); I extract global response (All api responses return this part).
and check code and message that I need
then I extract real data I want with final List<Ball> data = GSON.fromJson(result.data, new TypeToken<List<Ball>>() {}.getType()); and return it
I think the main bug/limit in Gson is it only check Generic Type only once for parent type and doesn't check it for generic Type[s]
and I think it's better that they (its Developers) add it to next versions

Related

How to convert List<?> to List<Object> in 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);

How to get generic? class from json response? If one object can contain many different classes

I want to extract json object with restassured, and response is generic like:
#Data
public class ExtractBase<T> {
private Class<T> result; // here I can expect different classes
private String targetUrl;
private Boolean success;
private String error;
private Boolean unAuthorizedRequest;
private Boolean __abp;
}
and I want extract it and get result each time with different class:
.extract().body().as(ExtractBase.class).getResult(); // and here I want to have possibility to choose class which should be exctracted depends on my request
I have tried to use TypeToken but with no result :(
any tips for extracting generic classes from JSON responses?
Use ObjectMapper to map from Objects response to custom type.
Example
objectMapper.readValue(json.getBoody(), YourType.class)
Remember, that objectMaper mustbe register module on methot objectMaper
private final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
and your response shouldby
private T result; - witout "class"

Parsing Json using FasterXML for generic elements

I´m using java spring boot to read data via REST api.
Incoming data is specified as followed:
public class Element<T>
{
public String error;
public Boolean state;
public T subelement;
}
public class Response<T>
{
public Element<T> element;
}
Generic Type T can be any generic class such as Boolean or complex classes.
In a simple sample I tried to parse an Response element:
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(value, Response<Boolean>.class);
Eclipse marks me the Response.class part as followed :
Boolean cannot be resolved to a variable
I´m used to use generics on fly in order to dynamicly interact in Delphi and .NET. There has never been the urge of generating classes or pass types since you can classify T such being a class, owning an empty constructor and json wrapper takes T arguments.
Since JacksonXML requires a Class I need to find a way to solve it.
I tried a few posts here on stack overflow to obtain data on following way:
ParameterizedType
JacksonXMLbind Javatype
any ideas?
Edit:
Well it could be based on the List Element that it looks differently.
Here is a sample of my json:
{"id":1,"state":true,"element":{"response":{"data":{"client_id":"sock1591427021","protocol_version":2}}},"errormsg":"OK"}
this part i use to convert the json :
private <T extends Object> APIResponse<T> readResponse(String data) {
try {
var value = objectMapper.readValue(data, new TypeReference<APIResponse<T>>() {
});
return value;
} catch (Exception e) {
logger.warn(e.getMessage());
return null;
}
}

How to abstract types of transformations

I have an program that already transform POJOs to xml via JAXB and this is done by adding a the elements to a list in this form
List<JAXBElement<?>> elementsToTransform = new ArrayList<JAXBElement<?>>();
In other method I just transform and populate the elementsToTransform as xml, the firm of this method is something like this
private List<JAXBElement<?>> transform(Student student)
Now I need to add a way to transform to Json via Gson and I want to abstract both declaration and method in order to support both formats but the implementation is pretty hardcoded to xml and I need to fit this solution as the way it is build with xml because this depends on other functionality that I do not want to modify so I would know if there is a way I can support both formats and which will be the better choice for this problem
At the end I would like to have something like this in the elementsToTransform list
JAXBElement(0)
JAXBElement(1)
JAXBElement(2)
String(3)(this will be Json)
Implement a wrapped class to incorporate both a String or JAXBElement. This can be identified by the enum Type. The convertion for each type can be implemented as an interface method.
interface IConvert{
JSONObject convert(WrappedObject o){
}
The Type Enum can be defined as follows
enum Type implements IConvert{
STRING {
public JSONObject convert(WrappedObject o){
String str = o.getString();
//use Gson here and convert;
return json;
}
},
JAXB {
public JSONObject convert(WrappedObject o){
JAXBElement jax = o.getJAX();
//use Gson here and convert;
return json;
}
};
}
Finally the WrappedObject will look like this :
class WrappedObject {
private String str;
private JAXBElement jax;
private Type type;
public WrappedObject(String str){
this.str=str
this.type=Type.STRING;
}
public WrappedObject(JAXBElement jax){
this.jax=jax
this.type=TYPE.JAXB;
}
public TYPE getType(){
return this.type;
}
public JSONObject convert(){
return type.convert(this);
}
//..other getters and setters
}
Hope this answers your design pattern question.

Pass parameterized type to method as argument

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.
Due to "type erasure" of Java generic types, you should use type cast explicitly as following instead:
Response<Video> videoResponse = (Response<Video>) parser.parse(reader, Response.class);
It will introduce a compilation warning but it's okay.

Categories