Java instanceof with Object - java

I have a spring boot project with Java 17. I am calling a third party API to get the list of records. If the records are present I do get the 200 OK response with list of records and in case records are not present I do get a 200 OK response with another JSON schema. To verify whether the response if of a type list of records or an error I am using instanceof but it is not working and always go to else condition.
HttpEntity<Void> request = new HttpEntity<>(httpHeaders);
ResponseEntity<Object> response = restTemplate.exchange(uri, HttpMethod.GET, request, Object.class);
if (response.getBody() instanceof ZohoError zohoError) {
return Collections.emptyList();
} else {
return (List<Leave>) response.getBody();
}
#Getter
#Setter
#ToString
#EqualsAndHashCode
public class ZohoError {
private String message;
#JsonAlias("errorcode")
private String errorCode;
#JsonAlias("Response status")
private int responseStatus;
}

As your third party API returns different schemas with same http code (which smells for me), you can't use RestTemplate to get needed object - you don't know the object type.
You can get RestTemplate response as String, parse it to JsonNode using ObjectMapper (Jackson). Then decide which schema is this by some telling attributes. And parse response string to defined type using ObjectMapper.

Related

Is there a way to retrieve objects with nested Lists via Spring's restTemplate

This is the object we are trying to retrieve:
// Lombok annotated
#Getter
#Setter
#ToString(callSuper = true)
public class GetTransactionsResponse {
public String name;
public List<Transaction> list;
}
We have an object that has metadata and a list of objects
We tried using spring's restTemplate in a method like the following:
public GetTransactionsResponse getTransactions(String token, Request request) {
var requestEntity = RequestEntity
.get(externalApiClient.getTransactionsPath())
.header(HttpHeaders.AUTHORIZATION, token)
.build();
return handleCall(requestEntity, GetTransactionsResponse.class);
}
private <T> T handleCall(RequestEntity<?> requestEntity, Class<T> clazz) {
try {
var result = restTemplate.exchange(requestEntity, clazz).getBody();
log.info("method: handleCall - requestEntity: {} - clazz: {} - result: {}", requestEntity, clazz, result);
return result;
} catch (Exception e) {
throw e
}
}
So we call the rest template but we are receiving null. Without nested data the previous method works but somehow returns null when using nested objects. Are we using rest template wrong?
First verify that your Rest API you created actually works. Test it with posman or ARC (Advanced Rest Client) - a plugin for chrome and see that you get the desired result. If it works, then you can send request to your API from any Http Client including Rest Template. If your API works but calling it from Rest Template doesn't then you will know that the problem is on your client side and you can try and figure out why. But first verify that your Rest API actually works and not returning null

Java #requestBody doesn't work, dto empty

For some reason java can't map DTO with requestBody and all values are default ones, as for request it works, with payload for ex. "{"productId":1,"commitment":6,"returnMonths":"2"}"
DTO
#Data
public class Request {
private int productId;
private int commitment;
private String returnMonths;
// contructers
}
Controller :
#PostMapping(value = "/calculate", consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String calculatePrice(#RequestBody Request request) {
productService.calculatePrice(request);
return "Success";
}
front request:
submit: async function() {
let request = {
productId: this.productSelected,
commitment: this.optionSelected,
returnMonths: this.input
};
let data = await getCalculation(request);
console.log(data);
}
DTO maps as:
productId : 0
commitment : 0
returnMonths : null
Tried an exact copy of your code and it worked when tested with Postman. This makes me think it's either something to do with the FE or maybe some issue in the service. I'd check if the Frontend really sends the data.
Try to annotation Request class with #AllArgsConstructor like:
#AllArgsConstructor
#Data
public class Request {
private int productId;
private int commitment;
private String returnMonths;
}
If your request body contains properties that is date such as LocalDateTime, make sure to format it in your DTO using #JsonFormat(pattern="") respecting the input value.

JSON Error on clientside when sending ResponseEntity from serverside after post request

I have an angular/spring boot webapp. When I send a create user postrequest the angular clientside app isn't able to read the body of the response entity that I send back after the operation. The error is:
{error: SyntaxError: Unexpected token U in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttp…, text: "User successfully created."}
I know that this is caused because the body content isn't in JSON format. But the error persists even when I add produces = "application/json" as an attribute to the #PostMapping annotation.
Heres the code:
#RestController
#RequestMapping("api/user")
public class UserController {
private final Log logger = LogFactory.getLog(this.getClass());
#Autowired
UserService userService;
#Autowired
UserDao userDao;
#PostMapping(path = "/create", produces = "application/json")
private ResponseEntity<String> createNewUser(#RequestBody UserCreateDTO newUser) {
logger.info("name is: " + newUser.getUserName());
Status status = userService.createUser(newUser);
return ResponseEntity.status(status.isSuccess() ?
HttpStatus.CREATED : HttpStatus.BAD_REQUEST).body(status.getInfo());
}
What should I do to solve this problem? I think it has something to do with the use of ResponseEntity. I could just send the status DTO object that I've made back instead, but I want to be able to manipulate the httpStatus code that is being sent back too, so that's why I want to use the ResponseEntity instead.
It looks like you're returning a string literal instead of a json object. The returning object when converted to json should be like
{
"status": "user created successfully"
}
try returning your full status object instead of status.getInfo() then your return object should look something like:
{
"info": "user created successfully"
}
and you can call status.info within your javascript to reference the return
and will have to change your return type to RepsonseEntity<Status>
In fact yes you are using ResponseEntity but with a String as body, because you are using:
.body(status.getInfo());
You need to specify an object in the body, you can create a POJO that will hold the message for you, wraps the status.getInfo() String, and it will be read as JSON.
The message POJO class:
public class MessageObject {
private String message;
//Constructors, getter and setter
}
Your return code would be:
return ResponseEntity.status(status.isSuccess() ?
HttpStatus.CREATED : HttpStatus.BAD_REQUEST).body(new MessageObject(status.getInfo()));

Spring Web Service: Adding Arrays/Collections to a response

Currently my webservice will return a response which queries for one specific record. A request has been made to allow for multiple similar records to be returned via the response message.
For Example:
I return name, address 1, address 2, postalcode for a specific person
They'd like to have a return of all names/addresses for the postalcode passed in. With that being said, my resultExtractor and response are doing single strings/ints currently. Is there any documentation out there explaining the process of using arrays with your response message?
Thanks!
Using spring, you can annotate the controller method with #ResponseBody.
Your java return type will be then be parsed and sent over the wire, if jackson is on your classpath then it will be converted to JSON.
Spring MVC ResponseBody docs
Similar question which has Java and xml config answers
The best way is to use Json in the response. So who make the request will need to convert the json into the right Object.
For example you can use Gson library of google: Gson Library
Ther is an example MVC controller that works in my project
#RequestMapping(value = "services/utente/getUtenteByUsername", method = RequestMethod.GET)
#ResponseBody
public String getUtenteDaUsername( #RequestParam("username") String username, Model model) {
utente = utenteBo.findByUsername(username);
String jsonResult = "";
if (utente != null) {
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
jsonResult = gson.toJson(utente);
return jsonResult;
}
else {
return null;
}
}

How to parse JSON Response to POJO with AndroidAnnotations?

I'm using AndroidAnnotations to build a Rest for an Android Application.
On the Serverside im using PHP, which send a json looking like :
{"tag":"register","success":0,"error":2,"msg":"User already existed","body":[]}
I have two POJOS :
User.java:
public class User implements Serializable {
private String name;
private String email;
private String password;
//getter and setter Methods
}
Response.java:
public class RegistrationResponse implements Serializable {
private String tag;
private int success;
private int error;
private String msg;
private String body;
//getter and setter Methods
}
Rest Client:
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
Response sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
Activity.java:
//User and Response are POJOs
Response result = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
But i got an Null Pointer Exception error on Activity.java. But if i change the return value of "sendUserRegistration" function to String all work. So my "Response" POJO seems not to be converted from AndroidAnnotations.
How can i convert the Rest Response to my "Response"-POJO using AndroidAnnotations?
You don't need to return the entire response object per rest call, just set the response to your custom object. Or you can also return a JsonObject also and use gson to convert it later on.
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
User sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
then just simply call
User newUser = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
AA relies on Spring Android RestTemplate to make the rest call. And in order to build requests and handle responses this lib uses converters. And to know which converter the RestTemplate should use, it checks the content-type response header.
As MappingJacksonHttpMessageConverter and GsonHttpMessageConverter handles only http response with content-type=application/json and your result is converted to string, I'm pretty sure you forgot to set this header in your php server. So it send the default one (ie: text/plain) which is only handle by StringHttpMessageConverter.
Also, the body field is an object in your json example, but in your POJO you declared it as a String. So parsing will fail on this point.

Categories