Spring Restful issue with list of length 1 - spring

We are having a json which shows a list of object as below:
skillCheckAnswers: [
{
optionId: 6786,
optionText: "Copy constructor",
optionWeight: "1.00"
},
{
optionId: 6787,
optionText: "Friend constructor",
optionWeight: "2.00"
},
{
optionId: 6788,
optionText: "Default constructor",
optionWeight: "3.00"
},
{
optionId: 6789,
optionText: "Parameterized constructor",
optionWeight: "4.00"
}
]
Which is ok as long as there are more than 1 object in the list. But if the list contains only 1 item then the json displays :
{
optionId: 6785,
optionText: "Friend class",
optionWeight: "4.00"
}
Please note that the second Json is not given as a List but as a single object ( missing square braces[] ).
Is there a way where I can output the list of length 1 with square braces?
Edit--
We can use foreach for the first JSON but foreach starts giving error if the number of object goes to 1. To handle this we are having an if else loop to check if the List is of length 1 or more than 1. If the list is of length then we handle this as a single obkect. This seems to be a stupid solution and I guess there would be an easy way out.
Can anyone tell me if this is even possible?

You should do something like:
From Spring controller:
#RequestMapping(method = RequestMethod.GET, value = "/myquestions")
public #ResponseBody
List<Answer> generateQuestions() {
List<Answer> answers = new ArrayList<Answer>();
Answer ans1 = new Answer();
ans1.setOptionId("6785");
ans1.setOptionText("Friend class");
ans1.setOptionWeight("4.00");
answers.add(ans1);
return answers;
}
The output is as follows:
[
{
"optionId": "6785",
"optionText": "Friend class",
"optionWeight": "4.00"
}
]
So far I understood, you problem is related to JSON converter. I have tested with Spring MappingJacksonHttpMessageConverter message converter. You can see Spring documentation for how to configure the JSON converter. http://static.springsource.org/spring/docs/3.0.0.M3/reference/html/ch18s02.html

You mentioned you use Spring. Doesn't Spring automatically look after JSON conversions for your the restful API it provides. You can put something like this is your conext file:
You can use org.springframework.http.converter.json.MappingJacksonHttpMessageConverter and set it up in your context file to do the JSON conversion for you.
Then you can use Spring MVC's #ResponseBody annotation to provide a restful service with having to handcode the JSON response.

Related

How to pass 2 or more variables using #PathParam in spring mvc? and suppose if I want to test it out using postman how to do that?

I'm trying to fetch value from db using JPA repository method
product findByIdNumberOrCifNumber(String idNumber , String cifNumber);
service class logic:-
public ResponseModel FindByCivIDOrCifNumber(String idNumber,String cifNumber) {
ResponseModel responseModel = new ResponseModel();
Optional<product> civId = Optional.ofNullable(productRepos.findByIdNumber(idNumber));
if (civId.isPresent()) {
responseModel.setResponse(productRepos.findByIdNumberOrCifNumber(idNumber,cifNumber));
} else {
errorModel errorModel1 = new errorModel();
enter image description here errorModel1.setErrorCode(productConstant.INVALID_REQUEST);
errorModel1.setErrorDescription("Requested Civil Id or CifNUmber is not present");
responseModel.setErrorModel(errorModel1);
}
return responseModel;
}
controller class:-
#GetMapping("/getByCifNoOrGetByIdNo")
public ResponseModel getProductByCifNoOrGetByIdNo(#RequestParam String idNumber,#RequestParam String cifNumber ) {
return productService.FindByCivIDOrCifNumber(idNumber,cifNumber);
}
post man:-
kindly help me out how to make it work:)
If you are looking for an answer to pass two or more path variables and test it with postman, you can try this.
#GetMapping("/api/mapping-name/{variable1}/{variable2}")
Here you will be getting two path variables which you can access by the syntax
#PathVariable("variable1") datatype variableName
Now in postman request url you can simply give the respective url, lets say:
https://localhost8080/api/mapping-name/:variable1/:variable2
which automaticaly will give you a key value section in the path variables section in the params with prepopulated key names as per the name you have given. In this case variable1 & variable2.
Give the respective value and it should work.

Spring Boot - ignore root element in decoding

A remote server sends a response in this format
{
"data": {
"item": {
a: ...
b: ...
}
}
}
In my spring application, I would like to decode this way directly. Instead of creating a Class for data.
record Item(String a, String b){}
Any idea how it can be done?
I already tried this. but no help.
spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true
#JsonRootName("data")
record Item(String a, String b){}
You can deserialize just the {"a": "aValue", "b": "bValue"} json part of your json using the JsonNode#at method that returns a JsonNode node that will be converted to your Item object with the ObjectMapper#treeToValue method like below:
//pointing to the /data/item node
JsonNode itemNode = mapper.readTree(json).at("/data/item");
//Item will contain a=aValue and b=bValue
Item item = mapper.treeToValue(itemNode, Item.class);

Spring boot: mockMvc testing controller: Expected: Entity, Actuall: null

How looks my json:
event object
{
...
"game": {
//game fields
}
}
...
}
I am trying to do:
event.setGame(new Game());
And check if there is my value by mockMvc
.andExpect(jsonPath("$.game").value(event.getGame()))
But i am getting error:
java.lang.AssertionError: JSON path "$.game"
Expected : Event(id= null, name= null, ...)
Actual :null
Why i am getting null, if i should get just empty game?
P.S. even if i set fields to game, i will get null
I make .andDo(print), and get :
Body = // event
{
"id":"5f087eec-8bf0-11eb-8dcd-0242ac130003",
"game":
{
"id":null,
"team1":null,
"team2":null,
"gameDate":null,
},
"user":
{
//user fields
}
}
How looks controller:
#GetMapping("/{id}")
public ResponseEntity<GetEventById> getEventById #PathVariable("id") String id) {
GetEventByIResponse dto= service.getEventById(id);
return ResponseEntity
.status(HttpStatus.OK)
.body(dto);
}
In my test i am creating GetEventByIResponse, how it looks:
public class Event {
private String id;
private Game game;
...
}
The JsonPath assert works as follows:
It first parses the path result into a Map/List/Object, to see if the origin was an array/object/simple type.
Then, if it's a Map (like in your case) it tries to parse the path result into the same type as the expected object.
Finally it compare the created object to the expected object using equals().
In your case I see several problems:
The AssertionError talks about an Event although you seem to hand in a Game.
We do not know if the serialization/deserialization works at all
Maybe you should try one of the following:
Place a breakpoint right here to watch the assert steps in your debugger:
Start with simple type comparisons:
.andExpect(jsonPath("$.id").value("foo"))
.andExpect(jsonPath("$.game.id").value("bar"))
Seems like the json path referencing the game child object is incorrect, try below:
.andExpect(jsonPath("$.event.game").value(event.getGame()))

How to force Spring HATEOAS resources to render an empty embedded array?

I have the following controller method:
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "session/{id}/exercises")
public ResponseEntity<Resources<Exercise>> exercises(#PathVariable("id") Long id) {
Optional<Session> opt = sessionRepository.findWithExercises(id);
Set<Exercise> exercises = Sets.newLinkedHashSet();
if (opt.isPresent()) {
exercises.addAll(opt.get().getExercises());
}
Link link = entityLinks.linkFor(Session.class)
.slash(id)
.slash(Constants.Rels.EXERCISES)
.withSelfRel();
return ResponseEntity.ok(new Resources<>(exercises, link));
}
So basically I am trying to get the expose a Set<> of Exercise entities for a particular Session. When the exercises entity is empty however I get a JSON representation like this:
{
"_links": {
"self": {
"href": "http://localhost:8080/api/sessions/2/exercises"
}
}
}
So basically there is no embedded entity, while something like the following would be preferrable:
{
"_links": {
"self": {
"href": "http://localhost:8080/api/sessions/2/exercises"
}
},
"_embedded": {
"exercises": []
}
}
any idea how to enforce this?
The problem here is that without additional effort there's no way to find out that the empty collection is a collection for Exercise. Spring HATEOAS has a helper class to work around this though:
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
EmbeddedWrapper wrapper = wrappers.emptyCollectionOf(Exercise.class);
Resources<Object> resources = new Resources<>(Arrays.asList(wrapper));
An EmbeddedWrapper allows you to explicitly mark objects to be added to the Resource or Resources as embedded, potentially even manually defining the rel they should be exposed under. As you can see above the helper also allows you to add an empty collection of a given type to the _embedded clause.
One can use the PagedResourceAssembler::toEmptyResource() method. For example, the following works:
Page<EWebProduct> products = elasticSearchTemplate.queryForPage(query, EWebProduct.class);
if(!products.hasContent()){
PagedResources pagedResources = pageAssembler.toEmptyResource(products, WebProductResource.class,baseLink);
return new ResponseEntity<PagedResources<WebProductResource>>(pagedResources, HttpStatus.OK);
}
I'd bet it works with other ResourceAssemblers as well.
If you have a Page< T >, you can convert it like this:
public static <T> PagedModel<EntityModel<T>> toModel(PagedResourcesAssembler<T> assembler,
Page<T> page) {
if (!page.isEmpty()) {
return assembler.toModel(page);
} else {
// toEmptyModel renders the _embedded field (with an empty array inside)
return (PagedModel<EntityModel<T>>) assembler.toEmptyModel(page, TenantSubscriptionResponseDto.class);
}
}
(You can obtain the PagedResourcesAssembler assembler by simply adding it as a parameter to the Controller method, and Spring will inject it).
Spring by default uses Jackson parser to serialize/deserialize json. As per http://wiki.fasterxml.com/JacksonFeaturesSerialization Jackson has a feature called WRITE_EMPTY_JSON_ARRAYS and its enabled by default. Maybe WRITE_EMPTY_JSON_ARRAYS is set to false in your config. please recheck your message converters configuration.

How to get backgrid-paginator speak with spring controller for sorting

We are using backbone.paginator;backgrid;backgrid-filter ;backgrid-paginator it works great we have small issue when trying sort - at server side mode -the grid by one (or more) of its columns
For Server side with are using Spring with org.springframework.data.web.PageableHandlerMethodArgumentResolver
When debugging the app it seems that the order from client side does NOT get parsed well on spring side and it falls to the default value
Two questions:
Does any one know what is the parameters that should be sent to PageableHandlerMethodArgumentResolver?
How can Backbone.PageableCollection can be tweak in order to achieve that?
Our Initialize Paginator Code
Backbone.PageableCollection.extend({
state : {
pageSize : 15,
firstPage : 0, /* Spring paging is 0-based */
},
mode : "server",
/**
* override default queryParams to fit spring pageable var names
*/
queryParams : {
pageSize:"size",
totalRecords:"totalElements",
sortKey:"sort",
order:"sort_order",
directions : { "-1": "asc", "1": "desc" }
},
})
P.S:
It seems that spring expect to get the data as array separated by comma instead regular. any idea how to it with backbone.paginator? or how to change spring to be able to parse paginator to support several of sorting parameters.
After doing some digging at Spring source PageableHandlerMethodArgumentResolver and at SortHandlerMethodArgumentResolver.java (can be found here) it Seems that Spring expect sort parameters to be with the format of
sort="column1,column2,column3...,order"&sort="column4,column5,...,order"
It seems that backbone.paginator does not support multiply ordering but in order to make paginator to support SortHandlerMethodArgumentResolver with singal ordering, one can override sync function to setup the vars.
Example
var sync = Backbone.PageableCollection.prototype.sync;
Backbone.PageableCollection.prototype.sync = function(method, model,
options) {
options.type = 'POST' // this help with unicode on tomcat and can be removed..
/**
* #override sync Add some code to support parsing of sorting parameters
* by SortHandlerMethodArgumentResolver
*/
if (!options.data) {
sync.apply(this, arguments)
}
var sortKey = _.result(this.queryParams,"sortKey");
if (!sortKey in options.data) {
sync.apply(this, arguments)
}
options.data[STR_SPRING_SORT] = options.data[sortKey]
var orderKey = _.result(this.queryParams,"order");
var STR_SPRING_SORT = "sort";
var STR_SEPERATOR = ",";
if (orderKey in options.data) {
options.data[STR_SPRING_SORT] = options.data[STR_SPRING_SORT] + STR_SEPERATOR
+ options.data[orderKey]
if (sortKey !== STR_SPRING_SORT) {
delete options.data[sortKey];
}
delete options.data["order"];
}
sync.apply(this, arguments);
};
Remark
One can force spring to change the comma and the sort string.

Resources