Spring ModelAttribute correct parsing - java

I have Spring controller with endpoint like:
#RequestMapping("/provider")
public ResponseEntity<Something> load(#ModelAttribute PageRequest pageRequest)
{
... //do something
}
where PageRequest is simple POJO:
class PageRequest
{
String[] field;
String[] value;
... // constructor getters settest
}
When I GET request like:
.../provider?field=commanders&value=John%2CBill%2CAlex then pageRequestdata is mapped:
field[0] = commanders;
value[0] = John
value[1] = Bill
value[2] = Alex
But when I GET request like:
.../provider?field=country&value=Australia&field=commanders&value=John%2CBill%2CAlex then pageRequestdata is mapped:
field[0] = country;
field[1] = commanders;
value[0] = Australia
value[1] = "John,Bill,Alex"
My question is why mapping is different for these requests, and can it be done for first request same as for the second. (comma %2C separated data map to single value).
used: Spring 3.x

I did some investigation and figured out that for first request value=John%2CBill%2CAlex Spring using org.springframework.format.supportDefaultFormattingConversionService class which under the hood has org.springframework.core.convert.support.StringToArrayConverter whose convert() method split your string by to array using comma as a separator.
You have 2 ways to resolve this issue:
Use ; instead of , as separator for you value (value=John;Bill;Alex)
Use different conversion service bean containing your own converter from String to String[]. For more details look at this answer

Related

How to add parameters in service call for #RequestParam backend?

If i have something like
#GetMapping(value = "/list")
public ResponseEntity<MyDTO> findStatusPerEC(#RequestParam final List<Long> numbersEC)
How do I add numbersEC in my url on the frontend? Is this query parameter?
I know this one was for old call that didn´t had query parameters and data was only a number (long)
return this.http.get<any>(URL_API + '/simulator/status/' + data);
But now I have to send a list of long values...may you help me?
return this.http.get<any>(URL_API + '/simulator/status/' + data);
Since you mentioned data is only a long type, what you are referring to here when you make the above request is a PathVariable it is slightly different to a RequestParam.
Path variables have the syntax: /simulator/status/:statusID where statusID is dynamic and extracts values from the URI.
Request parameters have the syntax: ?arg=val&arg2=val2 etc... and extract values from the request query string.
Solution
To answer your question, to send an array across as request parameters, you can do it like so:
?myparam=myValue1&myparam=myValue2&myparam=myValue3
As you can see, above myparam is unchanging, and the values are variable, hence the data within your list data structure.
So when you're making your request it will look similar to this:
Angular/Javascript
return this.http.get<any>(URL_API + '/list' + '?myparam=myValue1&myparam=myValue2&myparam=myValue3');
Java
#GetMapping(value = "/list")
public ResponseEntity<MyDTO> findStatusPerEC(#RequestParam final List<Long> numbersEC)
I hope this helps.

Spring Data Mongo returns lists instead of string

In my Spring Boot application, I am querying a collection of documents. I'm trying to perform an aggregate operation, to group similar fields together and count them.
The issue is that while the right values are being returned, they should be just normal strings "[ \"Test Name\"]" should be "Test Name"
I can solve this issue for one of the fields by using the #Id annotation but spring data Mongo does not like composite keys so I can not use that as a solution.
How can I return just the string value and not the values that are given below.
{
"exampleDateTime": 1392029442389,
"exampleName": "[ \"Test Name\"]",
"exampleDescription": "[ \"Test Description\"]",
"exampleCount": 1
}
So I have a java object like this
#Document(collection = "ExampleCollection")
public class Example{
private Date exampleDateTime;
private String exampleName;
private String exampleDescription;
private int exampleCount;
...getters and setters...
}
The Repository Implementation with Aggregation
public class ExampleRepositoryImpl implements ExampleRepositoryCustom {
#Autowired
MongoTemplate mongoTemplate;
#Override
public List<Example> countExampleByName() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("exampleName").addToSet("exampleName").as("exampleName").addToSet("exampleDateTime").as("exampleDateTime")
.addToSet("exampleDescription").as("exampleDescription").count().as("exampleCount")
AggregationResults<Example> groupResults = mongoTemplate.aggregate(
aggregation,"ExampleCollection", Example.class);
List<Example> result = groupResults.getMappedResults();
return result;
}
}
When you use addToSet, which returns collection type, spring calls toString() to convert it to string type ( note the quotes around [] ) as defined in your pojo
Replace your aggregation to use first with
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("exampleName")
.first("exampleName").as("ex‌​ampleName")
.first("e‌​xampleDateTime").as(‌​"exampleDateTime")
.first("exampleDescription").as("exampleDescription")
.count(‌​).as("exampleCount")
OR
Change your types in pojo to String[] or collection of string if you need to use addToSet

Array of Objects comes like String to Spring controller from Ajax

I pass array of items from JS to spring controller. I have this model in JS:
function Row() {
this.id = 0;
this.rulingValue = '';
this.rulingType = '';
this.dateStart = '';
this.dateEnd = '';
}
I have array of Rows - var jsonData = [];
Then I fill this array. And set to
var oMyForm = new FormData();
oMyForm.append("items", jsonData);
In Spring controller I expect this array like List<Item>
#Data
public class Item {
private String id;
private String rulingValue;
private String rulingType;
private String dateStart;
private String dateEnd;
}
#RequestParam("items") List<Item> items
But my items parameter arrive as String. How can I get this array like List<Item>?
Springs preferred way to send JSON to a Controller is as body payload rather than as form parameter. To do so, you just have to do three things:
Make sure you have a JSON library in the classpath, e.g. fasterxml.jackson.
Ensure your JSON payload is sent via POST request with HTTP Content-Type "application/json"
Annotate the receiving parameter in your Controller handler method with #RequestBody, e.g.
getItems(#RequestBody List items)
A complete example can be found here: http://www.beabetterdeveloper.com/2013/07/spring-mvc-requestbody-and-responsebody.html
If you stay with sending your data as form parameter, you will have to write a custom property editor along the lines indicated here: JSON parameter in spring MVC controller. It is much harder and requires extra code.

Issue with String array in Spring 3

I am trying to post a String[] from client to Spring 3. In Controller side i have defined the method like this.
#RequestMapping(value = "somemethod", method = RequestMethod.POST)
public ModelAndView exportSomething(#RequestParam("sentences") String[] sentences) {
//.. logic
}
The data that im sending looks like this
sentences: ["a","b,c","d"]
The problem is in server side the size of the sentences array is 4. It is splitting b and c as two different words.
Is this a issue with Spring or do i need change something the way i pass on the data?
Its a know issues I guess with Spring framework. See https://jira.springsource.org/browse/SPR-7963
Try sending data in this format.
sentences:"a;b,c;d"
Note in this case your delimiter is ; not , So you have sent a string which contains a list of
#RequestMapping(value = "somemethod", method = RequestMethod.POST)
public ModelAndView exportSomething(#RequestParam("sentences") String sentences) {
String[] sentenceArray = sentences.split(";");
for(String tempString:sentenceArray){
// perform what operation you want to perform
}
}
you will get an array of size three not four in this case.
The reason why your approach is not working is probably because you have used comma which is the default delimiter for an Array.

How to map a path suffix to a query parameter using Java Jersey?

Background: we have build a RESTful API using Jersey a while ago where we map the uri /items.json to a json array of ids and /items/{id}.json to the json object of a single item. Now we want to create a list with some meta data for each item and would like to use a selector like /items.data.json, similar to apache sling.
So far: I just extended the UriConnegFilter to parse the uri for additional suffixes, something like this:
public class UriSelectorFilter extends UriConnegFilter {
protected List<String> selectors; // this list is populated in the constructor
public ContainerRequest filter(ContainerRequest request) {
super.filter(request);
// search for suffix in last path segment, see http://java.net/projects/jersey/sources/svn/content/trunk/jersey/jersey-server/src/main/java/com/sun/jersey/api/container/filter/UriConnegFilter.java?rev=5034
final String[] suffixes = segment.getPath().split("\\.");
for (int i = suffixes.length - 1; i >= 1; i--) {
final String suffix = suffixes[i];
if(selectors.contains(suffix)) {
request.getQueryParameters().putSingle("selector", suffix);
final int index = path.lastIndexOf('.' + suffix);
path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();
suffixes[i] = "";
}
}
if (length != path.length()) {
request.setUris(
request.getBaseUri(),
request.getRequestUriBuilder().replacePath(path).build());
}
return request;
}
}
This filter works perfect, it finds the selector part of my uri and adds a query param to the request object. But in my Resource I added a #QueryParam attribute, which is only filled with the default value and not the added query value:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getItemsJSON(#DefaultValue("id") #QueryParam("selector") String selector) {
// query param is not filled with the selector that was found in the UriSelectorFilter
}
Does anybody have a suggestion how I can provide my resource with the selector that was detected? Is there a better way than using a QueryParam? (Note: if I add the query to my url like '?selector=something' then the attribute is filled correctly.)
Any help is very appreciated.
You need another argument that is annotated with #PathParam, and you need to specify in your #Path annotation (on the method or class) how to bind these bits together. For example, to deal with a path like /items/foobar42/data.json you might do this:
#GET
#Path("/items/{item}/data.json")
#Produces(MediaType.APPLICATION_JSON)
public Response getItemsJSON(#PathParam("item") String itemId,
#DefaultValue("id") #QueryParam("selector") String selector) {
// Now you've got an itemId and a possible selector...
}
Trying to do all the mapping with a filter… that seems difficult to me given that there's a nice declarative way of doing it instead. You can even specify a regular expression in the #Path to allow for matching a more complex variable section; I do that in my own code to create a method that can serve a whole hierarchical filesystem.
(Note that the {braced} item in the #Path should match the name in the #PathParam annotation, and you can have multiple items matched from the path if necessary; just use several #PathParam-annotated arguments.)

Categories

Resources