Parse a Complex Object in XML with Jersey - java

I am using Jersey and face an issue about how to parse a complex object to xml format, please help me about it, many thanks.
Here is the detail.
First, I make a entity container object like below:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class RestResponse {
//It can be any kinds of type, collection, single object etc
private Object data;
//... still have many properties
public RestResponse() {
}
public RestResponse(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
And here is one of my entity class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Entity1{
private String name;
private Map<String, Object> otherData = new HashMap<String, Object>();
public Entity1(){
this.name = "aaa";
otherData.put("address", "XXXXX");
otherData.put("age", 13);
//more...
this.otherData = otherData
}
public Entity1(String name, Integer age){
this.name = "aaa";
otherData.put("address", "XXXXX");
otherData.put("age", age);
this.otherData = otherData
}
public String getName() {
return name;
}
public Map<String, Object> getOtherData() {
return otherData;
}
}
Here is my resource class:
#Path("/test")
public class EntityResource{
#GET
#Path("test1")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test1() {
Entity1 entity = new Entity1();
return Response.ok(new RestResponse(entity)).build();
}
#GET
#Path("test2")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response test2() {
List entities = new ArrayList<Entity1>();
entities.add(new Entity1("E1"));
entities.add(new Entity1("E2"));
return Response.ok(new RestResponse(entities)).build();
}
}
Configure jersey with above code, it works fine when I require json format response, but for xml format response, I always get 500 error, am I missing something?

After some research, I found the solution, the answer is simple, I register JacksonXMLProvider.class as a XML provider, hope this can help other people.

Related

How to map response with objects instead of a list of objects with Spring Boot in Api Client

How should I create a class for an api response that returns objects instead of a list of objects? I'm using Spring Boot and RestTemplate and don't know how to go about it.
This is a response:
{
"status":200,
"data":{
"1":{
"id":"1",
"auth":"manual",
"confirmed":"1",
"policyagreed":"0",
"deleted":"0",
"suspended":"0",
"mnethostid":"1",
"username":"guest",
"password":"",
"idnumber":"",
"firstname":"Guest user",
"lastname":" ",
"email":"root#localhost",
"emailstop":"0",
"icq":"",
"skype":"",
"yahoo":"",
"aim":"",
"msn":"",
"phone1":"",
"phone2":"",
"institution":"",
"department":"",
"address":"",
"city":"",
"country":"",
"lang":"en",
"calendartype":"gregorian",
"theme":"",
"timezone":"99",
"firstaccess":"0",
"lastaccess":"0",
"lastlogin":"0",
"currentlogin":"0",
"lastip":"",
"secret":"",
"picture":"0",
"url":"",
"description":"",
"descriptionformat":"1",
"mailformat":"1",
"maildigest":"0",
"maildisplay":"2",
"autosubscribe":"1",
"trackforums":"0",
"timecreated":"0",
"timemodified":"1584114527",
"trustbitmask":"0",
"imagealt":null,
"lastnamephonetic":null,
"firstnamephonetic":null,
"middlename":null,
"alternatename":null,
"moodlenetprofile":null
},
"2":"...."
}
}
I tried something like this:
public class MoodleResponse {
private Integer status;
private Data data;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public class Data {
private HashMap<String, MoodleUser> map;
public HashMap<String, MoodleUser> getMap() {
return map;
}
public void setMap(HashMap<String, MoodleUser> map) {
this.map = map;
}
}
It doesn't work. I've never encountered anything like this.
you will need to create a model class here which has all attributes present in response and each attribute must be mapped with #JsonProperty
key here is to understand #JsonPropery annotation
for eg
public class Data{
#JsonProperty("id")
private String id;
#JsonProperty("auth")
private String auth;
}
and soo on

Spring Boot - JSON Object Array to Java Array

I have an endpoint in spring boot that consumes this JSON as an example:
{
"userId": 3,
"postBody": "This is the body of a post",
"postTitle": "This is the title of a post",
"created": null,
"tagList": ["tag1", "tag2", "tag3"]
}
The endpoint:
#RequestMapping(value="/newPost", method = RequestMethod.POST, produces="application/json", consumes = "application/json")
#ResponseBody
public ResponseEntity newPost(#RequestBody Map<String, Object> body) throws Exception {
I know the issue here is the Request body is being saved as a Map of objects which is fine for all the other attributes except the tagList. How can I get tagList to be an array of Strings in Java?
Thanks.
A mixutre of Ankur and Jose's answers solved this, thanks for the fast responses guys!
You should probably create a Java class which represents the input JSON and use it in the method newPost(.....). For example:-
public class UserPostInfo {
private int userId;
private String postBody;
private String postTitle;
private Date created;
private List<String> tagList;
}
Also, include the getter/setter methods in this class.
If you want to modify the behavior of JSON parsing, you can use Annotations to change field names, include only non-null values, and stuff like this.
If you don't want to use a custom POJO you could also just handle the deserialization into a Map yourself. Just have your controller accept a String and then use Jackson's ObjectMapper along with TypeReference to get a map.
#RequestMapping(value="/newPost", method = RequestMethod.POST, produces="application/json", consumes = "application/json")
#ResponseBody
public ResponseEntity newPost(#RequestBody String body) throws Exception {
ObjectMapper mapper = new ObjectMapper();
TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
HashMap<String,Object> map = mapper.readValue(body, typeRef);
}
The resulting HashMap will use an ArrayList for the tag list:
You can create a custom Java POJO for the request that uses String[] versus List<String>. Here I did it for you using the site jsonschema2pojo.
package com.stackoverflow.question;
import com.fasterxml.jackson.annotation.*;
import java.util.HashMap;
import java.util.Map;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"userId",
"postBody",
"postTitle",
"created",
"tagList"
})
public class MyRequest {
#JsonProperty("userId")
private int userId;
#JsonProperty("postBody")
private String postBody;
#JsonProperty("postTitle")
private String postTitle;
#JsonProperty("created")
private Object created;
#JsonProperty("tagList")
private String[] tagList = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("userId")
public int getUserId() {
return userId;
}
#JsonProperty("userId")
public void setUserId(int userId) {
this.userId = userId;
}
#JsonProperty("postBody")
public String getPostBody() {
return postBody;
}
#JsonProperty("postBody")
public void setPostBody(String postBody) {
this.postBody = postBody;
}
#JsonProperty("postTitle")
public String getPostTitle() {
return postTitle;
}
#JsonProperty("postTitle")
public void setPostTitle(String postTitle) {
this.postTitle = postTitle;
}
#JsonProperty("created")
public Object getCreated() {
return created;
}
#JsonProperty("created")
public void setCreated(Object created) {
this.created = created;
}
#JsonProperty("tagList")
public String[] getTagList() {
return tagList;
}
#JsonProperty("tagList")
public void setTagList(String[] tagList) {
this.tagList = tagList;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

How to retrieve data within a Map in java using findBy spring repository

I need help in creating a findBy spring query for the following scenario:
I have a JSON document with the following structure:
"data":{
"key1":"value1",
"key2":"value2"
}
In model, I have this"data" as a Map like,
Map<String, Object> data;
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
Now, I want to get the value2 from data using spring repository. I'm using couchbase for DB.
Any help would be really appreciable.
Thanks in advance.
#Entity
public class Obj{
private Integer id;
private String name;
//getter and setter
}
#Controller
public class ControllerClass{
#Autowired
private ObjService objService;
#GetMapping("/getObjectById/{id}")
#ResponseBody
public Map<String, Obj> getMapDetails(#PathVariable Integer id) {
Map<String, Obj> map = new HashMap<>();
map.put("data",objService.findById(id));
/*here you can able to N of times
Ex: map.put("data",service2.findById(id));
and etc...
*/
return map;
}
}
#Service
public Class ObjService{
public Obj findById(Integer id){
//logic
}
}
your Response will be like below:
{"data":{
"id":1,
"name":"value2"
}
}

Unable to make restTemplate call with Generics for nested Json

I am trying to make a restTemplate call for API testing. The json returned is a nested one with multiple levels.
{
"code": 200,
"data": {
"result": {
"publicId": "xyz"
}
}
}
I have the following classes acting as wrapper :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public abstract class RestCallResponse<T> {
private int code;
protected RestCallResponse(int code) {
this.code = code;
}
protected RestCallResponse(){}
#JsonProperty("data")
public Map<?, ?> getRestCallResponse() {
return ImmutableMap.of("result", getResult());
}
#JsonIgnore
protected abstract T getResult();
public int getCode() {
return code;
}
}
And then a SuccessRestResponse class extending this abstract class :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
public class SuccessRestResponse<T> extends RestCallResponse<T> {
#JsonProperty("result")
private T result;
public SuccessRestResponse() {
}
public SuccessRestResponse(T result) {
super(HttpStatus.SC_OK);
this.result = result;
}
protected T getResult() {
return this.result;
}
}
Then finally I have the actual data POJO :
public final class CreatedResponse {
#JsonProperty
private final EntityId publicId;
public CreateCreativeResponse(EntityId publicId) {
this.publicId = publicId;
}
}
In the test case, I am making a call as such :
ResponseEntity<SuccessRestResponse<CreatedResponse>> newResponse =
restTemplate.exchange(requestEntity, new ParameterizedTypeReference<SuccessRestResponse<CreatedResponse>>() {});
But I am getting the following error :
nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: null value in entry: result=null (through reference chain: com.inmobi.helix.test.api.dao.SuccessRestResponse["data"]);
Any suggestions? Where am I going wrong?
I solved the problem with a workaround. Still don't know what's wrong with the above piece of code though.
I got rid of the class RestCallResponse<T> and edited the field members in SuccessRestResponse<T> to look like this :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SuccessRestResponse<T> {
private int code;
private Map<String, T> data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Map<String, T> getData() {
return data;
}
#JsonIgnore
public T getResult() {
return data.get("result");
}
public void setData(Map<String, T> data) {
this.data = data;
}
}
This corresponds to the nested json while deserialization.
P.S. - Would still like to know what went wrong in my above code
though. As in, why did class hierarchy not work for me.

How to tell Swagger an attribute is a Map

I am developing a restful service with Jersey. However, I am using Swagger for documentation. My Model has a property with Type of Map. Swagger shows that this attribute as an Object (not a specific type). So how can I tell Swagger that this property is from type Map ?
public class Model {
private String name;
private Map<Integer, String> myMap;
public Model(){
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<Integer, String> getMyMap() {
return myMap;
}
public void setMyMap(Map<Integer, String> myMap) {
this.myMap = myMap;
}
}
The restful service:
#POST
#Path("/createBundle")
#Consumes({MediaType.APPLICATION_JSON})
#ApiOperation(value = "Create Bundle ",
notes = "",
response = Model.class)
public Model createBundle(Bundle bundle){
return new Model();
}
I need Swagger to show it as type of Map<Integer, String>.
Swagger shows the documentation as this image.
You can set a response type in the #ApiOperation annotation:
#ApiOperation(value = "Find thingies as Map",
notes = "Multiple thingies can be returned, <code>id</code> is the ID field",
response = java.util.Map.class)

Categories