Not Able to Cast the Object to Custom Object - java

I have the APIResponse class which extends the Object class like <T extends Object> but while getting the response body from the rest template getting the data into the Object class, not to the Book class.
If I try to fetch the data into the Book class it gives the null.
I have tried typecasting the response in The Book Object but no success.
ex.ApiResponse<Book>.
public void testCreate(){
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
RestTemplate restTemplate = new RestTemplate(factory);
BookDto bookDto = new BookDto("Pranav","dummy","dummy");
String url = "http://localhost:9090/books";
HttpEntity<BookDto> httpEntity = getHttpEntity(bookDto);
ResponseEntity<Object> book = restTemplate.exchange(url,HttpMethod.POST,httpEntity,Object.class);
//Able to Get the response body in Object but if I try to change it to the Book the response body is coming null.
}
private HttpEntity<BookDto> getHttpEntity(BookDto bookDto) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Accept",MediaType.APPLICATION_JSON_VALUE);
return new HttpEntity<>(bookDto,headers);
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class APIResponse<T extends Object> implements Serializable {
/**
* status & message fields have not setter. They are assigned value when
* initial by APIStatus parameter
*/
private int status;
private String message;
private T data;
public APIResponse(APIStatus apiStatus, T data) {
if (apiStatus == null) {
throw new IllegalArgumentException("APIStatus must not be null");
}
this.status = apiStatus.getCode();
this.message = apiStatus.getDescription();
this.data = data;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
public enum APIStatus {
// Common status
OK(200, null);
private final int code;
private final String description;
private APIStatus(int s, String v) {
code = s;
description = v;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
}
#Component
public class ResponseUtil {
private APIResponse<Book> createResponse(APIStatus apiStatus, Object data) {
return new APIResponse(apiStatus, data);
}
// base method
public ResponseEntity<APIResponse<Book>> buildResponse(APIStatus apiStatus, Object data, HttpStatus httpStatus) {
return new ResponseEntity(createResponse(apiStatus, data), httpStatus);
}
public ResponseEntity<APIResponse<Book>> successResponse(Object data) {
return buildResponse(APIStatus.OK, data, HttpStatus.OK);
}
}
#RestController
public class BookController {
#Autowired
BookService bookService;
#Autowired
protected ResponseUtil responseUtil;
#GetMapping("/books")
ResponseEntity<APIResponse<Book>> read(){
return responseUtil.successResponse(bookService.findAll());
}
}
I expect the response body result into the Book Object, not to the General Object class.

Related

Post API using Spring Boot returns null values for nested json

I am new to Spring boot, and was trying to create a post api to post the following json to.
However, when I do a get on the api, for the nested elements, null values are displayed.
Json Request:
{
"messageType": "abcd",
"messageVersion": "1.1.0",
"p_messageVersion": "1.095",
"acsTransID": "6834628",
"p_formValues_BRW": {
"action": "http://10.10.65.96:8080/CORE/Test.htm",
"correctFormData": "1234",
"incorrectFormData": "0000",
"cancelFormData": "true"
}
}
Response on Doing a Get:
[{"acsTransID":"6834628","p_messageVersion":"1.095","messageVersion":"1.1.0","messageType":"abcd","p_formValues_BRW":{"action":null,"correctFormData":null,"incorrectFormData":null,"cancelFormData":null}}]
My Model Object
public class Product {
#JsonProperty("acsTransID")
private String acsTransID;
#JsonProperty("p_messageVersion")
private String p_messageVersion;
#JsonProperty("messageVersion")
private String messageVersion;
#JsonProperty("messageType")
private String messageType;
#JsonProperty("p_formValues_BRW")
private p_formValues_BRW p_formValues_BRW;
public Product(p_formValues_BRW p_formValues_BRW) {
this.p_formValues_BRW=p_formValues_BRW;
}
public Product() {
}
public String getacsTransID() {
return acsTransID;
}
public void setacsTransID(String acsTransID) {
this.acsTransID = acsTransID;
}
public String getp_messageVersion() {
return p_messageVersion;
}
public void setp_messageVersion(String p_messageVersion) {
this.p_messageVersion = p_messageVersion;
}
public String getmessageVersion() {
return messageVersion;
}
public void setmessageVersion(String messageVersion) {
this.messageVersion = messageVersion;
}
public String getmessageType() {
return messageType;
}
public void setmessageType(String messageType) {
this.messageType = messageType;
}
public p_formValues_BRW getp_formValues_BRW() {
return p_formValues_BRW;
}
public void setp_formValues_BRW(p_formValues_BRW p_formValues_BRW) {
this.p_formValues_BRW = p_formValues_BRW;
}
/*
public Product withPFormValuesBRW(PFormValuesBRW pFormValuesBRW) {
this.pFormValuesBRW = pFormValuesBRW;
return this;
}*/
}
class p_formValues_BRW {
#JsonProperty("action")
private String action;
#JsonProperty("correctFormData")
private String correctFormData;
#JsonProperty("incorrectFormData")
private String incorrectFormData;
#JsonProperty("cancelFormData")
private String cancelFormData;
public String getaction() {
return action;
}
public void setaction(String action) {
this.action = action;
}
public String getcorrectFormData() {
return correctFormData;
}
public void setcorrectFormData(String correctFormData) {
this.correctFormData = correctFormData;
}
public String getincorrectFormData() {
return incorrectFormData;
}
public void setincorrectFormData(String incorrectFormData) {
this.incorrectFormData = incorrectFormData;
}
public String getcancelFormData() {
return cancelFormData;
}
public void setcancelFormData(String cancelFormData) {
this.cancelFormData = cancelFormData;
}
}
My Controller
#RestController
public class ProductServiceController {
private static Map<String, Product> productRepo = new HashMap<>();
#RequestMapping(value = "/products", method = RequestMethod.POST)
public ResponseEntity<Object> createProduct(#RequestBody Product product, p_formValues_BRW p_formValues_BRW) {
product.setp_formValues_BRW(p_formValues_BRW);
productRepo.put(product.getacsTransID(), product);
// productRepo.put(product., PFormValuesBRWRepo);
return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED);
}
#RequestMapping(value = "/products")
public ResponseEntity<Object> getProduct() {
return new ResponseEntity<>(productRepo.values(), HttpStatus.OK);
}
}
What I am doing wrong. Also, would it be better to use JPARepositories and #autowired.
You don't need to add p_formValues_BRW in createProduct() function's parameter separately, because on your JSON Request you pass p_formValues_BRW as nested object.
So on your controller when you hit "/products" you will get p_formValues_BRW in Product, so function after changes look like this:
#RequestMapping(value = "/products", method = RequestMethod.POST)
public ResponseEntity<Object> createProduct(#RequestBody Product product) {
product.setp_formValues_BRW(product.getp_formValues_BRW());
productRepo.put(product.getacsTransID(), product);
// productRepo.put(product., PFormValuesBRWRepo);
return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED);
}
As you already giving p_formValues_BRW in request body, you need not add it in Product.
#RequestMapping(value = "/products", method = RequestMethod.POST)
public ResponseEntity<Object> createProduct(#RequestBody Product product) {
productRepo.put(product.getacsTransID(), product);
// productRepo.put(product., PFormValuesBRWRepo);
return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED);
}

Failed to bind JSON response to GSON annotated POJO

I intended to bind a JSON string to POJO annotated with GSON, the JSON response is from the ReSTFUL service to list all countries: http://services.groupkt.com/country/get/all
the response is fine, which looks like
{
"RestResponse": {
"messages": [
"More webservices are available at http://www.groupkt.com/post/f2129b88/services.htm",
"Total [249] records found."
],
"result": [
{
"name": "Afghanistan",
"alpha2_code": "AF",
"alpha3_code": "AFG"
},
{
"name": "Ă…land Islands",
"alpha2_code": "AX",
"alpha3_code": "ALA"
},
...
]
}
}
The POJO Country and its associated classes were created using this tool:http://www.jsonschema2pojo.org/ and they look like:
Country.java
public class Country implements Serializable{
#SerializedName("RestResponse")
#Expose
private RestResponse restResponse;
public RestResponse getRestResponse() {
return restResponse;
}
public void setRestResponse(RestResponse restResponse) {
this.restResponse = restResponse;
}
}
RestResponse.java
public class RestResponse implements Serializable{
#SerializedName("messages")
#Expose
private List<String> messages = null;
#SerializedName("result")
#Expose
private List<Result> result = null;
public List<String> getMessages() {
return messages;
}
public void setMessages(List<String> messages) {
this.messages = messages;
}
public List<Result> getResult() {
return result;
}
public void setResult(List<Result> result) {
this.result = result;
}
}
Result.java
public class Result implements Serializable{
#SerializedName("name")
#Expose
private String name;
#SerializedName("alpha2_code")
#Expose
private String alpha2Code;
#SerializedName("alpha3_code")
#Expose
private String alpha3Code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAlpha2Code() {
return alpha2Code;
}
public void setAlpha2Code(String alpha2Code) {
this.alpha2Code = alpha2Code;
}
public String getAlpha3Code() {
return alpha3Code;
}
public void setAlpha3Code(String alpha3Code) {
this.alpha3Code = alpha3Code;
}
}
The code below however failed to bind the JSON string to the GSON annotated POJOs - the restResponse is NULL, so are the message and result. Can anyone tell me what went wrong?
#SpringBootApplication
public class App implements CommandLineRunner
{
private static Logger log = LoggerFactory.getLogger(App.class);
/*
* boiler plate code
* */
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
/*
* Configuration section
* */
#Bean
public RestTemplate newRestTemplate(){
RestTemplate rt = new RestTemplate();
return rt;
}
/*
* public APIs section
* */
#Autowired
private RestTemplate restTemplate;
#Override
public void run(String... args) throws Exception {
String url = "http://services.groupkt.com/country/get/all";
ResponseEntity<String> res = restTemplate.getForEntity(url, String.class);
log.info("{}",res.getBody());
GsonHttpMessageConverter msgConverter = new GsonHttpMessageConverter();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
msgConverter.setGson(gson);
restTemplate.getMessageConverters().add(msgConverter);
Country country = restTemplate.getForObject(url, Country.class);
RestResponse resp = country.getRestResponse();
List<Result> l = resp.getResult();
for(Result r : l){
log.info("country name = {}",r.getName());
}
}
}
I managed to update the code like below and it works now:
RestTemplate rt = new RestTemplate();
String url = "http://services.groupkt.com/country/get/all";
ResponseEntity<String> resp = rt.getForEntity(url, String.class);
assertEquals(resp.getStatusCode(), HttpStatus.OK);
Gson gson = new GsonBuilder().create();
Country c = gson.fromJson(resp.getBody(), Country.class);
still don't know why the code below didn't work, though.
Country country = restTemplate.getForObject(url, Country.class);

Convert json into an object of a class that contains a template variable using gson

My Api response looks like this.
{
"status" : 1,
"message" : "Some Message",
"data" : {
}
}
My Response class looks like this. The type of data changes depending on the request being made.
public class Response<T>{
#Expose
#SerializedName("status")
private Integer status;
#Expose
#SerializedName("message")
private String message;
#Expose
#SerializedName("data")
private T data;
//Getters and Setters
}
Question 1. How to use gson to parse this json?
Response<ClassA> response = new Gson().fromJson(jsonString, ??);
Question 2. How would i write Parcelable implementation for this class.
dest.writeInt(status == null ? 0:status);
dest.writeString(message);
??
You need to do some changes in your Model structure. To do this..
Create a BaseDTO which is nothing but your Response class and extend your BaseDTO with your ClassA.
BaseDto.class
public class BaseDto {
#Expose
#SerializedName("status")
protected Integer status;
#Expose
#SerializedName("message")
protected String message;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
ClassA.class
public class ClassA extends BaseDto implements Parcelable {
String name;
protected ClassA(Parcel in) {
name = in.readString();
status = in.readByte() == 0x00 ? null : in.readInt();
message = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
if (status == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeInt(status);
}
dest.writeString(message);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<ClassA> CREATOR = new Parcelable.Creator<ClassA>() {
#Override
public ClassA createFromParcel(Parcel in) {
return new ClassA(in);
}
#Override
public ClassA[] newArray(int size) {
return new ClassA[size];
}
};
}
Use this site to generate Parcelable class http://www.parcelabler.com/
to parse using gson you just need to pass it's type to it..
I have written a function to simplify this..
Save these in a Util class
public static <T> String convertObjectToStringJson(T someObject, Type type) {
Gson gson = new Gson();
String strJson = gson.toJson(someObject, type);
return strJson;
}
public static <T> T getObjectFromJson(String json, Type type) {
Gson gson = new Gson();
if (json != null) {
if (json.isEmpty()) {
return null;
}
}
return gson.fromJson(json, type);
}
example to use these function:
ClassA classA = Util.getObjectFromJson(strJson, new TypeToken<ClassA>() {}.getType());
String jsonClassA = Util.convertObjectToStringJson(objClassA, new TypeToken<ClassA>() {}.getType());
Answering my own question.
Question 1:
Response<ClassA> response = new Gson().fromJson(jsonString, new TypeToken<Response<ClassA>>(){}.getType());
Question 2:
Changed class to this.
public class Response<T extends Parcelable>{
#Expose
#SerializedName("status")
private Integer status;
#Expose
#SerializedName("message")
private String message;
#Expose
#SerializedName("data")
private T data;
//Getters and Setters
#Override
public void writeToParcel(Parcel dest, int flags) {
if (data != null) {
dest.writeString(data.getClass().getName());
dest.writeParcelable(data, flags);
} else dest.writeString(null);
dest.writeString(message);
dest.writeInt(status);
}
}
protected Response(Parcel in) {
String className = in.readString();
if (className != null) {
try {
data = in.readParcelable(Class.forName(className).getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
message = in.readString();
status = in.readInt();
}

Deserializing JSON-valued string

We are using Spring Boot to expose a REST endpoint which is called by a dumb client which delivers us the following:
{
"timestamp": "2016-08-16T14:30.000Z",
"data": "{\"amount\":1,\"product\":\"BASIC PRODUCT\"}"
}
We've created the following objects:
#JsonDeserialize(builder = Message.Builder.class)
public final class Message {
private final String timestamp;
private final Data data;
public String getTimestamp() {...}
public Data getData() {...}
#JsonPOJOBuilder
public static final class Builder {
private String timestamp;
private Data data;
public Builder withTimestamp(final String timestamp) {...}
public Builder withData(final Data data) {...}
}
}
and
#JsonDeserialize(builder = Data.Builder.class)
public final class Data {
private final String product;
private final int amount;
public String getProduct() {...}
public int getAmount() {...}
#JsonPOJOBuilder
public static final class Builder {
private String product;
private int amount;
public Builder withProduct(final String product) {...}
public Builder withAmount(final int amount) {...}
}
}
and exposed the endpoint as
#RequestMapping(consumes = "application/json", method = POST)
public ResponseEntity<?> receive(#RequestBody Message message) {
/// ...
}
but control doesn't even reach the receive method and fails with 400 BAD REQUEST. I believe this has to do with the fact that data is a JSON-valued string. Does Jackson provide any annotation that I can use to force the JSON-valued string to be deserialized as an instance of Data?
The key is in public Builder withData() method of Message.Builder.class to explicitly parse JSON-valued string to Data type. Change the method parameter to String instead of Data and call ObjectMapper().readValue(JSON-valued string, Data.class) to deserialize it into Data.
For example like this:
public Builder withData(final String jsonValue) throws JsonParseException, JsonMappingException, IOException {
Data data = new ObjectMapper().readValue(jsonValue, Data.class);
this.data = data;
return this;
}
For the clarity sake here you are my whole POJOs:
Message:
public final class Message {
private final String timestamp;
private final Data data;
private Message(Builder builder){
this.timestamp = builder.timestamp;
this.data = builder.data;
}
public String getTimestamp() {...}
public Data getData() {...}
#JsonPOJOBuilder
public static final class Builder {
private String timestamp;
private Data data;
private static ObjectMapper mapper = new ObjectMapper();
public Builder withTimestamp(final String timestamp) {
this.timestamp = timestamp;
return this;
}
public Builder withData(final String jsonValue) throws JsonParseException, JsonMappingException, IOException {
Data data = mapper.readValue(jsonValue, Data.class);
this.data = data;
return this;
}
public Message build() {
return new Message(this);
}
} // Builder
}
Data:
public final class Data {
private final String product;
private final int amount;
private Data(Builder builder){
this.product = builder.product;
this.amount = builder.amount;
}
public String getProduct() {...}
public int getAmount() {...}
#JsonPOJOBuilder
public static final class Builder {
private String product;
private int amount;
public Builder withProduct(final String product) {
this.product = product;
return this;
}
public Builder withAmount(final int amount) {
this.amount = amount;
return this;
}
public Data build() {
return new Data(this);
}
} // Builder
}
Hope it helps.

Json mapping with JaxB annotation using Jackson

How to serialize this class using Jackson
package com.progressivebeef.service.response;
#XmlRootElement(name = "response")
#XmlSeeAlso({ User.class, Profile.class,MenuItem.class,Feedlot.class,Document.class,FeedlotDocument.class })
public final class PBResponse {
private Integer status = FAILURE;
private String code;
private String message;
private Integer totalRecords;
private List<Model> list = new ArrayList<Model>();
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
#XmlElementWrapper(name = "PBBeans")
#XmlAnyElement
public List<Model> getList() {
return list;
}
public void setList(List<Model> list) {
this.list = list;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Integer getTotalRecords() {
return totalRecords;
}
public void setTotalRecords(Integer totalRecords) {
this.totalRecords = totalRecords;
}
/**
* #author tiqbal
* Resets the response.
*/
public void reset(){
this.status = FAILURE;
this.list = new ArrayList<Model>();
this.code = null;
this.message = null;
this.totalRecords = null;
}
}
Jackson is not picking up #XmlElementWrapper #XmlSeeAlso annotations, also Jackson is not mapping #XmlRootElement annotation. I am using Jackson 1.9.0. Jackson is putting elements in the list but not mapping root element of POJO classes.
Here is sample method.
package com.progressivebeef.service.impl;
#Service("/ActivityServiceImpl/")
#Path("/activityservice/")
public class ActivityServiceImpl implements ActivityService {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Override
public Response inputJson(User user ) {
System.out.println("user ");
user.setUser_name("check user name");
Profile profile = new Profile();
profile.setFirst_name("abc");
profile.setLast_name("khan");
user.setProfile( profile );
PBResponse response = new PBResponse();
response.getList().add(user);
return Response.ok(response).build();
}
}
The response it generating is '{"response":{"status":0,"PBBeans":[{"user_name":"check user name","password":"click123","user_role_key":2,"profile":{"first_name":"abc","last_name":"khan","tableName":"pb_profile","pk":"profile_id"},"tableName":"pb_user","pk":"user_id"}]}}'
not picking up the bean's root name inside PBBeans tag.
Hope this helps. Basically, you need to set the WRAP_ROOT_VALUE to true in your mapper.

Categories

Resources