Why isn't JSON being unmarshalled into my Java class? - java

I'm using Spring Boot and RestTemplate to GET data from an in-house service. The JSON I'm getting back as the response isn't being unmarshalled into my object and I'm unable to figure out why. (I'm consuming other similar REST APIs and those are working, so I've done this before.)
The code for making the request is standard stuff:
public ChecklistResponse getCurrentStatus(String studentId) {
RestTemplate restTemplate = createRestTemplate();
HttpHeaders httpHeaders = this.createHeaders();
String url = this.createItemStatusUrl(studentId);
ResponseEntity<ChecklistResponse> responseEntity = restTemplate.exchange(url, HttpMethod.GET,
new HttpEntity<>(httpHeaders), ChecklistResponse.class);
ChecklistResponse checklistResponse = responseEntity.getBody();
return checklistResponse;
}
The entire Object tree is large, so I can't really show it here. ChecklistResponse is simple enough, though:
public class ChecklistResponse {
private HttpStatus httpStatus;
private String responseType;
private Response response;
public ChecklistResponse() {
}
... getters/setters, equals, hashCode, toString ...
}
The start of the JSON response looks like this:
{
"httpStatus": {
"code": "200",
"description": "OK"
},
"responseType": "namespaceURI=\"http://bmeta.xxx.com/student/studentChecklistV0.xsd\" element=\"studentChecklist\"",
"response": {
"studentChecklist": {
"identifier": {
I set up interceptors to verify that I'm getting a response back. I ran it with the interceptors disabled, too, so that I know it's not consuming the response (though I'm using the interceptors successfully elsewhere and the object are properly unmarshalling.)
My question is, how can I debug this problem? I've tried enabling Jackson debugging, but that yields nothing. I've set breakpoints throughout the ChecklistResponse class, but it doesn't hit any of those.

Most likely you have forgot to add public parameterless constructor, add one like this:
public ChecklistResponse(){}
you should add these constructors to all classes in the object tree.
Another thing to look at is if the issue is a result of the values in the JSON object, ex, unescaped quotation etc .. try to make a unit test with a mocked object with trivial values but none empty/null ones and see if that test can pass or not, case sensitive issues sometimes cause this to fail as well, set break points in the getters/setters, these should be called in the process, make sure they are public as well as the classes themselves, final thing I can think of is the circular reference, make sure you don't have that as well in your object as this will cause issues.

Make sure that the model object ChecklistResponse correctly represents the map for the JSON object returned by the API you are testing with.

Related

Spring Boot sends BAD_REQUEST if expected request body contains DTO with only one value

I'm developing a web application with Spring Boot and Angular and currently writing a REST test call to test a PUT method for my Spring Boot Controller. The controller expects to receive a request body with a DTO that wraps a single boolean value. After checking the boolean value, it is supposed to update some stuff and send back a message.
My problem is that everytime I run the test I get a 400 BAD_REQUEST error, even when running the application and testing it "live".
This is what the backend expects to receive:
#Data
#AllArgsConstructor
public class ValidityRequest {
boolean isValid;
}
The weird thing is that it all works perfectly if I add a second value to the ValidityRequest object like such:
#Data
#AllArgsConstructor
public class ValidityRequest {
boolean isValid;
boolean someUnusedValue;
}
I have tried numerous different combinations of possible solutions to make it work with just the one value inside the wrapper just to understand the problem (e.g. implements Serializable, #JsonProperty, renaming the value to valid to help avoid the problematic of JSON needing a "getIsValid" getter, using Object type Boolean, completely replacing the boolean with a String, writing all Constructors, getters and setters without Lombok), but nothing worked.
The only way to get the REST test to pass, was to send a single boolean instead of wrapping it into a DTO. In the live application this solution didn't work, because the boolean would always reach the backend as false, even when I sent it as true from the frontend.
The only way to get both the test and the application to work was, as I stated, to add a second value (whatever, String, boolean, LocalDate, all did the trick) even if that value wasn't used.
Note: I would like a solution for the single value DTO to work, but more than that I would like to know what is causing the BAD_REQUEST error so I can gain more knowledge on this issue.
My controller looks like this:
#PutMapping("/check/{id}")
public ResponseEntity<Object> checkValidityAndUpdate(
#PathVariable final String id,
#RequestBody final validityRequest ValidityRequest,
final WebRequest request) {
return execute(request, () -> ResponseEntity.ok(validityService.checkValidityAndUpdate(id, validityRequest)));
}
My REST test call looks like this:
final HttpEntity<Object> request = new HttpEntity<>(validityRequest, authenticator.getAuthorizationHeader());
ResponseEntity<String> response = testRestTemplate.exchange(
new URI(BASE_URL + "/check/" + id),
HttpMethod.PUT,
request,
String.class);

How to consume a Spring HAL/HATEOAS API in Java using purely Jackson, not Spring

We are trying to create a Java client for an API created with Spring Data.
Some endpoints return hal+json responses containing _embedded and _links attributes.
Our main problem at the moment is trying to wrap our heads around the following structure:
{
"_embedded": {
"plans": [
{
...
}
]
},
...
}
When you hit the plans endpoint you get a paginated response the content of which is within the _embedded object. So the logic is that you call plans and you get back a response containing an _embedded object that contains a plans attribute that holds an array of plan objects.
The content of the _embedded object can vary as well, and trying a solution using generics, like the example following, ended up returning us a List of LinkedHashMap Objects instead of the expected type.
class PaginatedResponse<T> {
#JsonProperty("_embedded")
Embedded<T> embedded;
....
}
class Embedded<T> {
#JsonAlias({"plans", "projects"})
List<T> content; // This instead of type T ends up deserialising as a List of LinkedHashMap objects
....
}
I am not sure if the above issue is relevant to this Jackson bug report dating from 2015.
The only solution we have so far is to either create a paginated response for each type of content, with explicitly defined types, or to include a List<type_here> for each type of object we expect to receive and make sure that we only read from the populated list and not the null ones.
So our main question to this quite spread out issue is, how one is supposed to navigate such an API without the use of Spring?
We do not consider using Spring in any form as an acceptable solution. At the same time, and I may be quite wrong here, but it looks like in the java world Spring is the only framework actively supporting/promoting HAL/HATEOAS?
I'm sorry if there are wrongly expressed concepts, assumptions and terminology in this question but we are trying to wrap our heads around the philosophy of such an implementation and how to deal with it from a Java point of view.
You can try consuming HATEOS API using super type tokens. A kind of generic way to handle all kind of hateos response.
For example
Below generic class to handle response
public class Resource<T> {
protected Resource() {
this.content = null;
}
public Resource(T content, Link... links) {
this(content, Arrays.asList(links));
}
}
Below code to read the response for various objects
ObjectMapper objectMapper = new ObjectMapper();
Resource<ObjectA> objectA = objectMapper.readValue(response, new TypeReference<Resource<ObjectA>>() {});
Resource<ObjectB> objectB = objectMapper.readValue(response, new TypeReference<Resource<ObjectB>>() {});
You can refer below
http://www.java-allandsundry.com/2012/12/json-deserialization-with-jackson-and.html
http://www.java-allandsundry.com/2014/01/consuming-spring-hateoas-rest-service.html

Creating a generic error object to indicate partial success of data retrieval in Spring Boot

My question should be quite simple.
I have a Spring Boot REST API.
#GetMapping("/customer")
public Customer getCustomer() {
return service.getCustomer()
}
My controller returns List<Customer> and works well. But now I want to return another object with the errors that can happen when gathering the customers. Let's say it's called GenericErrorClass
So to return this I need to create a class that groups List and GenericErrorClass and return that class, right?
that will work but now I have Account, Product, etc... I don't think it makes sence create a class for each one of them.
How can I build a custom object without creating classes and return that as json from the rest controller?
Don't do that.
Throw your exception, or let it escape from your call stack. Use #ControllerAdvice (or #RestControllerAdvice) with #ExceptionHandler instead.
You can extend the abstract class ResponseEntityExceptionHandler, and provide additional handling methods. On the long run that will matter for a clean application design.
If you intend to return the error with a status code of 200, I'd like to understand why. I witness developers serving out responses for errored requests with 200 just because handling the HTTP error in another code branch at client side seems "difficult".
At first, you should know that there is already a similar class org.springframework.http.ResponseEntity, which object you could return as a response of your API. It can wrap your response body - List, Account, Product, etc. with the possibility to override default Http status.
So, based on this example you can write your own simple wrapper class like this:
public class Response<T>
{
private GenericErrorClass error;
private T body;
// constructors + getters + setters
}
and when your API method should return List<Customer> you will return Response<List<Customer>> , in the same way other objects.
However, I would recommend you to catch exceptions and send detailed error message + corresponding error code to API client. This is much better from a design point of view.
To implement this here is a good read.
If you have time to think about the design of your API, I would recommend this guide.
Generally you would return error information when the request is not succeeded, that coincides with a 4/5xx error code. Usually with Spring you manage this situation with exception handlers, as shown here where you can define a different response body. There is also another good practice: use envelopes to manage all responses, I will show you an example
{
status: 200,
message: 'user retrieved',
result:
[
{foo1:"bar1"},
.....
]
}
OR
{
status: 400,
message: 'bad request',
error:
{
reason : "wrong field xxx in request"
}
}
in this way clients can process the request and provide useful info to users. To do this you have to define a class that is used for all responses and should encapsulate the result or the error

Using Retrofit / GSON how can I handle responses that are sometimes empty and other times not

I am using retrofit with GSON. The web service I am connecting to will at times return an empty payload to the client. I didn't build it this way. The problem is that this causes a json parse exception when GSON tries to parse the empty payload. How could I handle this? Is there a way to make the callback have an null model object?
So for example the server response might be "" or it might be:
{
"foo":{"id":"123","description":"abcd"}
}
I also have in java:
#Data // This comes from lombok to generate setter,getter,no args constructor...
public class Foo{
private int id;
private String description;
}
My retrofit service looks like this:
public interface MyService{
#POST("/poorly/designed/api/foo")
void getFoo(#Header("Authorization") String auth, Callback<Foo> callback);
}
Disclamer: I just typed up the most simple example I could so forgive me if I wrote something that might not be exactly syntactically correct. You get the picture.
Retrofit should generally just null out any missing values. The problem with your code is that the server returns an object with a Foo inside rather than just a Foo. Try adding another class
class FooContainer {
Foo foo;
}
and then have your API call return a FooContainer instead of a Foo.

Can I use JSON data in RequestMapping in Spring MVC

I'm thinking the answer here is probably no, but just in case.
I'm doing something like this:
#RequestMapping(value="data.json", params="query=overview")
public String getOverview(#RequestBody MyRequest myRequest) {
[...]
return "overview";
}
#RequestMapping(value="data.json", params="query=detail")
public String getDetail(#RequestBody MyRequest myRequest) {
[...]
return "detail";
}
and the client is POSTing JSON data, which is deserialized by Jackson on the way in and bound to the MyRequest parameter, all working nicely.
However, I don't like the query type having to be specified in the URL of the requests. What I would like is to include the query parameter in the JSON object and use that to drive the #RequestMapping. Is this possible?
If not, I guess I will implement a single mapping and have that method delegate to others based on the incoming data, but that feels like a step in the wrong direction.
What you are trying to do does not work out of the box.
If you don't like the param why don't you add the qualifier to the URL like so:
#RequestMapping("/overview/data.json")
#RequestMapping("/detail/data.json")
If you absolutely need the functionality you mention, you could implement a custom RequestMappingHandlerMapping that would do what you want by extending that class as is done here.
It's not possible if you remove the params. You have to have something distinct between the two mappings. If you are intent on getting rid of the params, best you could do is have a single method/mapping and call your services or whatever other logic you have according to what the value of query is in your MyRequest object.
#RequestMapping(value="data.json")
public String getOverviewOrDetail(#RequestBody MyRequest myRequest) {
if (myRequest.getQuery().equalsIgnoreCase("overview")) {
[...]
return "overview"
} else if(myRequest.getQuery().equalsIgnoreCase("detail")) {
[...]
return "detail"
}
}
Since both methods are unmarshalling to the same object, you don't really need two separate methods/mappings.

Categories