Cast an Array in to an Array of specific objects in Java - java

I have a response class - MyResponse as below to call the server using Retrofit2. resource is an array of objects.
public class MyResponse {
#SerializedName("success")
#Expose
private Boolean success;
#SerializedName("resource")
#Expose
private Array[] resource;
public ApiResponse(Boolean done, Array[] resource) {
this.done = done;
this.resource = resource;
}
//getters and setters
}
In the Activity, I created an Array as below:
MyResponse decodedResponse = response.body();
Array[] catsList = decodedResponse.getResource();
And I have a Model class Category as below:
public class Category {
#SerializedName("id")
#Expose
private Integer id;
#SerializedName("category")
#Expose
private String category;
//getters, setter and constructor
}
Each object of above catsList array follows above category model(with id and category keys). Now how I make an array of Category (Category[]) from above catsList array?
I can't do it as Category[] catsList = decodedResponse.getObject(); because I want to create more arrays for other models too. Therefore I am planning to create a common MyResponse class as above and cast it's generated array to specific model when needed.
Also following method(after changed Array[] =to=> Object[] in MyResponse) is not supported. It gives java.lang.ClassCastException: java.lang.Object[] cannot be cast to my.package.Category[] exception.
Object[] catsList = decodedResponse.getObject();
Category[] catsListCooked = (Category[]) catsList;
EDIT:
My JSON response from server has same style for every request types and uses one model for one request type. Because Retrofit wants to know which model class will use for the call in order to make a call as below.
#GET()
Call<MyResponse> getCategories(#Url String url);
Note: Please correct me if I can do this in another way. Because I call as mentioned above, I need to have a class in order to make a call.
So what I want to do is create a main response(MyResponse as above) which will receive the response and then use each model to fetch received data according to the request type. It is not possible to use the model directly when making a call because I have a custom JSON response from server.
i.e. If used model directly, then it gives errors such as expected BEGIN_ARRAY but provide BEGIN_OBJECT or expected BEGIN_OBJECT but provide BEGIN_ARRAY.
As commented below, from suggested duplicates, this suggestion by ytg looks like ok to achieve the solution. But using Arrays.copyOf() as suggested there, it seems it is not creating Category[] array to be useful since it gives "...cannot be stored in destination array of type..." error. Also other solutions from that question are not doable in my case.

Related

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"

Object not being able to map to POJO class

I am getting a response, which I converted to Pojo class with one field of type Object. Now when I am trying to cast the Object type to another Pojo class its throwing the error :
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to SecondClass
Code :
FirstClassResponse firstClassResponse = (FirstClassResponse) convertJSONToObject(firstClassResponseJson, FirstClassResponse.class);
//jsonToObject method
public static Object convertJSONToObject(String jsonRequest, Class objectClassType) throws Exception {
Object object = gson.fromJson(jsonRequest, objectClassType);
return object;
}
Here, firsClass object when printed gives following result :
FirstClassResponse [modifiedResponse=null, response={id=123, username=abc, balance=0.0, currencycode=EUR, created=2021-03-30 16:31:54, agent_balance=0.0, sessionid=123}]
Now, the error happens in the following line :
SecondClassResponse modifiedResponse = (SecondClassResponse) firstClassResponse.getResponse();
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to SecondClassResponse
I am sharing the POJO for FirstClassResponse and SecondClassResponse :
public class FirstClassResponse{
private SecondClassResponse modifiedResponse;
private Object response;
//getter, setter
}
public class SecondClassResponse{
private String id;
private String username;
private double balance;
private String currencycode;
private String created;
private double agent_balance;
private String sessionid;
//getter, setter
}
private Object response;
Make this a SecondClassResponse, not an Object. With it being an Object, GSON doesn't know that this should be a SecondClassResponse, so it just shoves the map in there as a Map, which obviously can't be cast.
The entire point of using GSON is to turn everything into specific objects so you can use it in a more Java like way. If you store something as an Object when converting from GSON, you're almost always doing it wrong.
That FirstClassResponse is completely superfluous; use SecondClassResponse instead.
Just look at the JSON ...and then explain to me how to map as FirstClassResponse?
And you've not even object-relational mapping (as the GSON converter does), but you're parsing.
Perhaps gson.fromJson cannot convert the attribute class of the class before. You can try to take out firtClassResponse.getResponse() and do the conversion separately

java jersey 2 how to process regular html form array with post?

I have a jersey endpoint
#Path(value = "/testfields")
#POST
#Consumes({"application/x-www-form-urlencoded"})
public Response acceptFields(#BeanParam MyWrapper initialWrapper,String entity) {
//.......
}
MyWrapper is:
class MyWrapper{
#FormParam("param1")
private String param1;
#FormParam("inners")
private List<MyInnerWrapper> inners;
//..getters setters
}
class MyInnerWrapper{
#FormParam("innerParam1")
private String innerParam1;
//.... getters setters
}
I have request POST entity string:
param1=aaa&inners[0]["innerParam"]=bbb&inners[1]["innerParam"]=nnn
The issue is I can obtain parent`s field param1 with #BeanParam annotation, but I can't obtain inner list of custom classes, why? How to process regular html form array? I need to get my class evaluated with its inner classes in my endpoint. I tried array, list of objects, nothing works. Thanks
AFAIK, that type of syntax is not supported in Jersey. If you want to use custom types for #FormParam, then you need to meet the requirements of at least one those listed in the Javadoc. One options is to just have a constructor with a String argument. For example
public class MyInnerWrapper {
private final String innerParam;
public MyInnerWrapper(String param) {
this.innerParam = param;
}
public String getInnerParam() {
return innerParam;
}
}
Then all you need to do is send this request
param1=blahparam&inners=first&inners=second&inners=third
Then you can have
#FormParam("inners")
private List<MyInnerWrapper> inners;
There's no need for an [] to specify indices. Duplicates are allowed. That's why we are allowed to have lists and arrays for the #FormParam types.
Now if you want to have more than one property and then try to map them like you currently are, I don't think that will work. At least not in Jersey. I would recommend maybe looking into using JSON if you need more complex types. Otherwise just stick to simple keys.

JSON Parsing errors with RESTeasy/JAXB

I'm trying to make a call to the iTunes REST service that returns information about the genres defined in iTunes via a RESTeasy client. The JSON object returned by this call looks something like this:
{
"35":{
"name":"iPod Games",
"id":"35",
"url":"https://itunes.apple.com/us/genre/ipod-games/id35"
},
"36":{
"name":"App Store",
"id":"36",
"url":"https://itunes.apple.com/us/genre/ios/id36?mt=8"
}
}
I've defined my response object model like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonIgnoreProperties(ignoreUnknown = true)
public class ITunesGenre implements Serializable {
private static final long serialVersionUID = 4330727214147295490L;
#XmlElement
private String name = null;
#XmlElement
private String id = null;
...
}
However, when I make the call via my RESTeasy client, however, I get serialization errors. I believe it is due to the fact that this is not a true List or array of objects. Instead, it seems like each entry has an "identifier" on it (in the example above, the "35" or "36").
Given a JSON object like this, how do I map this so that the RESTeasy client can deserialize it? I've not encountered objects of this format before. I obviously can't hard-code each identifier, since there will be several and they could potentially change.
You can see the full JSON object returned by this call (it's sizable) by clicking here. You'll see that this object structure is found throughout this object, rather than using simple Lists or Arrays of objects.
Any ideas? I'd really appreciate any help you can give.
The response is a Map so root element is not your ITunesGenre class but the Map.I suppose that it is clear how to modify response object.
I suppose it could look something like this (though i haven't tested it)
#XmlRootElement
public class Response implements Serializable
{
public Response(){
}
private java.util.Map<String, Genre> genres = new java.util.HashMap<String, Genre> ();
}
public class Genre implements Serializable {
private static final long serialVersionUID = 4330727214147295490L;
public Genre(){
}
#XmlElement
private String name = null;
#XmlElement
private String id = null;
...
}

Handling Multiple Query Parameters in Jersey

In the web service I'm working on, I need to implement a URI with query parameters which look like /stats?store=A&store=B&item=C&item=D
To break it down, I need to be able to use query parameters to specify data from multiple/all stores and data for multiple/all items from those stores. So far I have been able to implement one query argument just fine in order to pull item data, but I'm lost as far as to how to implement more queries, and can't seem to find the resources I had seen before which deal with this implementation.
What I have so far in my method is along the lines of
#GET
#Path("stats")
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "item") final String item)
{
/**Run data using item as variable**/
return someStringOfData
}
which works well for one item, and will return all data if I don't type the parameter in the URI. However, I am unsure how to handle any more parameters than this.
Update:
I have figured out how to use 2 different parameters by simply adding a second argument to the method like so:
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "store") final String store,
#DefaultValue("All") #QueryParam(value = "item") final String item)
The question remains of how to implement multiple values of the same parameter.
If you change the type of your item method parameter from String to a collection such as List<String>, you should get a collection that holds all the values you are looking for.
#GET
#Path("/foo")
#Produces("text/plain")
public String methodImCalling(#DefaultValue("All")
#QueryParam(value = "item")
final List<String> item) {
return "values are " + item;
}
The JAX-RS specification (section 3.2) says the following regarding the #QueryParam annotation:
The following types are supported:
Primitive Types
Types that have a constructor that accepts a single String argument.
Types that have a static method named valueOf with a single String argument.
List<T>, Set<T>, or SortedSet<T> where T satisfies 2 or 3 above.
List<String> items=ui.getQueryParameters().get("item");
where ui is declared as a member in the rest resource like so :
#Context UriInfo ui;
the downside is that it doesn't appear in the methods arguments at all.
Some libs like axios js use the square brackets notation when sending a multi-value param request: /stats?store[]=A&store[]=B&item[]=C&item[]=D
To handle all cases (with or without square brackets) you can add another param like this:
public String methodImCalling(
#QueryParam(value = "store") final List<String> store,
#QueryParam(value = "store[]") final List<String> storeWithBrackets,
#QueryParam(value = "item") final List<String> item,
#QueryParam(value = "item[]") final List<String> itemWithBrackets) {
...
}
Inspecting each of the arguments checking for null.

Categories