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;
...
}
Related
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"
I am building Spring Boot webflux REST API functionality that needs to work with data containing few Java type's (let's consider String, Integer, Double for example) information as part of JSON request/responses. Attribute representing Java type must be persistable inside mongodb as well (should not be problem once JSON can work with such attribute). I have following model class and type enumeration which is used by REST API to serialize/deserialize JSON message's.
#Getter
#ToString
#EqualsAndHashCode(exclude = "id")
#Document(collection = "core_scheme")
#JsonDeserialize(builder = SchemeModel.Builder.class)
#Builder(builderClassName = "Builder", toBuilder = true, setterPrefix = "with")
public class SchemeModel {
#Id
private final String id;
#Field(name = "userId") private final String userId;
#Field(name = "date") private final String creationDate;
#Field(name = "properties") private final Map<String, SchemeTypes> properties;
}
public enum SchemeTypes {
INTEGER, STRING, DOUBLE
}
Serialization and deserialization work's well. Now the problem is that when i want to resolve real Java type's stored inside Map<String, SchemeTypes> properties map i need to do mapping similar to this (just abstraction not real code):
SchemeTypes.INTEGER => Java Integer class
SchemeTypes.STRING => Java String class
SchemeTypes.DOUBLE => Java Double class
Is there any more simple way to represent Java type's stored inside model class and used within serialized/deserialized JSON file's so i can directly use it to deduce Java type without additional validation that it's valid Java type. For example if type's enumarated inside mentioned enum would have exactly same naming as real Java type's i could do following without any mapping:
public void deduceClass(SchemeTypes type) {
Class myClass = Class.forName(type.toString());
}
Note that i am looking for a solution which would work out of the box (i don't have to validate type's provided by user). If such solution would be harder to implement as mentioned mapping i will stick with mapping.
If you weren't saving this entity I could say you can actually directly map the SchemeTypes into corresponding class like following
public enum SchemeTypes {
INTEGER(Integer.class), STRING(String.class), DOUBLE(Double.class);
private final Class clazz;
private SchemeTypes(Class clazz){
this.clazz = clazz;
}
public Class getClazz(){
return clazz;
}
}
But as you are saving this it could cause some issue to deserialize.
Maybe you can save not the SchemaType instance directly but just the name of enum to overcome this like following
private final Map<String, String> properties;
and find the corresponding clazz value with a static method on this class like following
public static Class findClazzFor(String schemeTypeName){
return SchemeTypes.valueOf(schemeTypeName).getClazz();
}
Nevertheless I think cleanest solution would be keeping the SchemeType class instance mapping somewhere as a one-to-one map. And retrieve the corresponding class for provided schemeType as in the getClazz method above.
I'm using Gson to cast my class to Json, I have a field called payload
which must to be my class itself.
My class is as follow:
public class MyClass {
private Long id;
private String name;
private Object payload;
}
But when I use it as follow:
MyClass myClassObj = MyClass()
myClassObj.setId(1L);
myClassObj.setName("Example");
myClassObj.setPayload(myClassObj);
And I see the result of:
String result = new Gson().toJson(myClassObj);
The result does not contain payload data object.
{"id":1, "name":"Example"}
I need something like:
{"id":1, "name":"Example", "payload": {"id":1, "name":"Example"}}
Thanks in advance.
Any help will be useful.
You will need to change it to be defined as such
public class MyClass {
private Long id;
private String name;
private MyClass payload = null;
}
Gson serialized parameters that are not transient. Object does not have the "id" and "name" parameters you are wanting to serialize. MyClass does. Ensure that payload's default value is null otherwise you may have an infinite loop on your hands when serializing to json.
I found a solution, Object can be anything, so before to set payload of my class I made the follow:
myClassObj.setPayload(new Gson.fromJson(myClassObj.toString(), myClassObj.class);
Note:
In MyClass I have override toString method as follow:
#Override
public String toString() {
return new Gson().toJson(this);
}
If someone has a better solution, feel free to post it.
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.
ive got this exception.
My Object looks like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ObjectDTO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -8545841080597549468L;
#XmlElement(name="objectId")
private String objectId;
#XmlElement(name="owner")
private String owner;
#XmlElement(name="objectName")
private String objectName;
Constructor, getter/setter, toString, Hash...
Now I invoke my service method with:
ClientResponse postRes = service.path("rest").path("object").accept(MediaType.APPLICATION_XML).post(ClientResponse.class, object);
When I do
System.out.println(postRes.getEntity(ObjectDTO.class));
it works fine. But when I try to do a get operation on my object like:
String string = postRes.getEntity(ObjectDTO.class).getObjectId();
I get the exception.
javax.ws.rs.WebApplicationException: javax.xml.bind.UnmarshalException - with linked exception: [org.xml.sax.SAXParseException: Premature end of life.]
Whats wrong?
Looks like you are trying to read the entity twice (the second time the entity stream is already consumed). Instead of calling postRes.getEntity() twice, call it just once, store the result in a variable and then operate on that variable.