I have a class
Entity implements org.joda.beans.Bean {
String name;
double weight;
....
}
I have an endpoint like this:
#RequestMapping(value = "CREATE", method = POST)
public void createEntity(#RequestBody Entity entity) {
logic.createEntity(entity);
}
Frontend sends a Json string to this endpoint:
{"name": "Bob", "weight":"99.7"}
Now I want to have another endpoint to update the entity.
It accepts json strings where only part of the attributes are set:
{"weight":"99.8"}
Its signature could be like this:
#RequestMapping(value = "UPDATE", method = POST)
public void updateCompany(#RequestBody Map<String, String> update1) {
Map<String, Object> update2 = deserialize(Entity.class,update1);
logic.updateEntity(update2);
}
The question is, how to implement the method deserialize which takes pair of Strings ["weight","99.8"] and converts it to the pair String-Object: ["weight", Double.valueOf("99.8")] because it knows, that the type of weight is double as declared in the class Entity. Such conversion was done already while preparation of arguments for the method createEntity(), now I want to extract it as a separate method call.
Deserialize to Map<String,String>
Iterate over each entry
Crete String->Double pair
Put to Map<String,Double>
Related
In order to increase performance, my API is going to receive an array of string that I need to convert to an array of objects.
My array looks like this:
List<String> listPersons = ["1, Franck, 1980-01-01T00:00:00, 00.00", "2, Martin, 1989-01-01T00:00:00, 00.00"];
How could I easily convert it to a list of Persons (List), if possible using Java 8 so I don't have to create a loop and manually explode the String?
class Person {
private Integer id;
private String name;
private Date dateOfBirth;
// getter and setter
}
Ideally I'd like to automate this directly using SpringBoot - Using a custom converter such as:
public class StringToPersonConverter implements Converter<String, Person> {
#Override
public Person convert(String from) {
String[] data = from.split(",");
return new Person(Integer.parseInt(data[0]), data[1], new Date(data[2]));
}
}
Declaring the converter:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCreditCardConverter());
}
}
And ideally map it from my controller directly?
#RequestMapping(value = "/insertPersons", method = RequestMethod.POST)
#ResponseBody
public String savePersons(#RequestBody List<Person> listPersons) {}
Unfortunately, it doesn't seem to detect my converter and it's throwing an error ;/
Any idea? Thanks
As a side note : String[] data = from.split(","); will not work with , as character separator because values use this character, for example : 1980-01-01T00:00:00, 00.00.
In order to increase performance, my API is going to receive an array
of string that I need to convert to an array of objects.
Here is a non answer. JSON is a format that consumes very few memory (literally characters) to convey the structure. So you don't need and even don't have to concatenate in a single String distinct information that here represent difference Person.
Designing an API that waits for unstructured JSON/text and restructuring the JSON/text in the backend in an anti pattern : it is not more efficient, it makes the API unclear and add boiler plate code both in the front and the back end.
I have a REST controller with a RequestMapping that looks like this:
#RequestMapping(method = RequestMethod.GET)
public List<MyDTO> search(SearchParameters searchParameters) {
// ...
}
and call it like that: /data/search?name=some%20value&....
searchParameters is populated, but the values are not being urldecoded. So instead of setting searchParameter's attribute name to "some value" it is "some%20value". How can I instruct Spring to urldecode these values?
One possible solution is to use a Map and have their names stored statically in a class, like :
#RequestMapping(method = RequestMethod.GET)
public List<MyDTO> search(#RequestParam Map<String,String> parameters) {
String name = parameters.get(SearchParameters.NAME);
// ...
}
or use the Map to build the Object SearchParameters:
#RequestMapping(method = RequestMethod.GET)
public List<MyDTO> search(#RequestParam Map<String,String> parameters) {
SeachParameters searchParameters = new SearchParameters(parameters);
// ...
}
Okay, the problem was not actually the decoding, but the encoding. It was encoded in a test case, although it wouldn't have needed to be. So the URL looking like /data/search?name=some%20value should really have been /data/search?name=some value with actual spaces.
I'm trying to get my query params in a DTO like in this question but my DTO has always null value.
Is there anything wrong in my code ? I made it as simple as possible.
Queries:
GET http://localhost:8080/api/test?a=azaz => null
POST http://localhost:8080/api/test with {"a":"azaz"} => "azaz"
Controller with a GET and a POST:
#RestController
#RequestMapping(path = {"/api"}, produces = APPLICATION_JSON_VALUE)
public class MyController {
// GET: dto NOT populated from query params "?a=azaz"
#RequestMapping(method = GET, path = "test")
public #ResponseBody String test(TestDto testDto){
return testDto.toString(); // null
}
// POST: dto WELL populated from body json {"a"="azaz"}
#RequestMapping(method = POST, path = "test")
public #ResponseBody String postTest(#RequestBody TestDto testDto){
return testDto.toString(); // "azaz"
}
}
DTO:
public class TestDto {
public String a;
#Override
public String toString() {
return a;
}
}
Thanks !
Full Spring boot sample to illustrate it
The problem is that you are missing setter for the field.
public void setA(String a) {
this.a = a;
}
should fix it.
I'm assuming that you have done required configuration like having Jackson mapper in the class path, consume json attribute, getter and setter in DTO classes etc.
One thing missed here is, in RequestMapping use value attribute instead of path attribute as shown below
#RequestMapping(method = POST, value= "/test", consumes="application/json")
public #ResponseBody String postTest(#RequestBody TestDto testDto){
return testDto.toString();
}
And, make sure that you set content-type="application/json" while sending the request
I think what you are trying to do is not possible. To access the query Parameter you have to use #RequestParam("a"). Then you just get the String. To get your object this way you have to pass json as Parameter. a={"a":"azaz"}
Kind regards
I want to create a page where a person sees a list of users and there are check boxes next to each of them that the person can click to have them deleted.
In my MVC that consumes a REST API, I want to send a List of User objects to the REST API.
Can the #RequestParam annotation support that?
For example:
#RequestMapping(method = RequestMethod.DELETE, value = "/delete")
public #ResponseBody Integer delete(
#RequestParam("users") List<Users> list) {
Integer deleteCount = 0;
for (User u : list) {
if (u != null) {
repo.delete(u);
++deleteCount;
}
}
return deleteCount;
}
In the MVC client, the url would be:
List list = new ArrayList<User>();
....
String url = "http://restapi/delete?users=" + list;
Request parameters are a Multimap of String to String. You cannot pass a complex object as request param.
But if you just pass the username that should work - see how to capture multiple parameters using #RequestParam using spring mvc?
#RequestParam("users") List<String> list
But I think it would be better to just use the request body to pass information.
Spring mvc can support List<Object>, Set<Object> and Map<Object> param, but without #RequestParam.
Take List<Object> as example, if your object is User.java, and it like this:
public class User {
private String name;
private int age;
// getter and setter
}
And you want pass a param of List<User>, you can use url like this
http://127.0.0.1:8080/list?users[0].name=Alice&users[0].age=26&users[1].name=Bob&users[1].age=16
Remember to encode the url, the url after encoded is like this:
http://127.0.0.1:8080/list?users%5B0%5D.name=Alice&users%5B0%5D.age=26&users%5B1%5D.name=Bob&users%5B1%5D.age=16
Example of List<Object>, Set<Object> and Map<Object> is displayed in my github.
Just a reminder, any List of custom objects might require custom converters to be registered, like:
#Bean
public Converter<String, CustomObject> stringToCustomObjectConverter() {
return new Converter<>() {
#Override
public CustomObject convert(String str) {
return new ObjectMapper().readValue(str, CustomObject.class);
}
};
}
#Bean
public Converter<String, List<CustomObject>> stringToListCustomObjectConverter() {
return new Converter<>() {
#Override
public List<CustomObject> convert(String str) {
return new ObjectMapper().readValue(str, new TypeReference<>() {
});
}
};
}
So you can cover custom cases like:
/api/some-api?custom={"name":"Bla 1","age":20}
/api/some-api?custom={"name":"Bla 1","age":20}&custom={"name":"Bla 2","age":30}
/api/some-api?custom=[{"name":"Bla 1","age":20},{"name":"Bla 2","age":30}]
where: #RequestParam("custom") List customObjects
Suppose a hyperlink is clicked and an url is fired with the following parameter list myparam=myValue1&myparam=myValue2&myparam=myValue3 . Now how can I capture all the parameters using #RequestParam in spring mvc?
My requirement is I have to capture all the params and put them in a map.
Please help!
#RequestMapping(value = "users/newuser", method = RequestMethod.POST)
public String saveUser(#RequestParam Map<String,String> requestParams) throws Exception{
String userName=requestParams.get("email");
String password=requestParams.get("password");
//perform DB operations
return "profile";
}
You could use RequestParam in the above mentioned manner.
It seems you can't get
Map<String,String>
because all your params have same name "myparam"
Try this instead:
public ModelAndView method(#RequestParam("myparam") List<String> params) { }
To get all parameters at once try this:
public ModelAndView postResultPage(#RequestParam MultiValueMap<String, String> params)
This feature is described in the #RequestParam java doc (3. Paragraph):
Annotation which indicates that a method parameter should be bound to a web request parameter. Supported for annotated handler methods in Servlet and Portlet environments.
If the method parameter type is Map and a request parameter name is specified, then the request parameter value is converted to a Map assuming an appropriate conversion strategy is available.
If the method parameter is Map<String, String> or MultiValueMap<String, String> and a parameter name is not specified, then the map parameter is populated with all request parameter names and values.
As of Spring 3.0, you can also use MultiValueMap to achieve this:
A rudimentary example would be:
public String someMethod(#RequestParam MultiValueMap<String,String> params) {
final Iterator<Entry<String, List<String>>> it = params.entrySet().iterator();
while(it.hasNext()) {
final String k = it.next().getKey();
final List<String> values = it.next().getValue();
}
return "dummy_response";
}
If anyone is trying to do the same in Spring Boot, use RequestBody in place of RequestParam
Spring mvc can support List<Object>, Set<Object> and Map<Object> param, but without #RequestParam.
Take List<Object> as example, if your object is User.java, and it like this:
public class User {
private String name;
private int age;
// getter and setter
}
And you want pass a param of List<User>, you can use url like this
http://127.0.0.1:8080/list?users[0].name=Alice&users[0].age=26&users[1].name=Bob&users[1].age=16
Remember to encode the url, the url after encoded is like this:
http://127.0.0.1:8080/list?users%5B0%5D.name=Alice&users%5B0%5D.age=26&users%5B1%5D.name=Bob&users%5B1%5D.age=16
Example of List<Object>, Set<Object> and Map<Object> is displayed in my github.
You can use for multiple Params as such
public String saveUser(#RequestParam("email") String userName, #RequestParam("password") String password) throws Exception{
//your code
//perform DB operations
return "profile";
}
For params with same name, you can use MultiValueMap<String ,String>. Then all the values would be present as List
You can use multiple #RequestParam annotations as shown below.
#RequestParam(value="myparam1", required = true) <Datatype> myparam1,
#RequestParam(value = "myparam2", required = false) <Datatype> myparam2,