Spring JSON request body not mapped to Java POJO - java

I'm using Spring to implement a RESTful web service. One of the endpoints takes in a JSON string as request body and I wish to map it to a POJO. However, it seems right now that the passed-in JSON string is not property mapped to the POJO.
here's the #RestController interface
#RequestMapping(value="/send", headers="Accept=application/json", method=RequestMethod.POST)
public void sendEmails(#RequestBody CustomerInfo customerInfo);
the data model
public class CustomerInfo {
private String firstname;
private String lastname;
public CustomerInfo() {
this.firstname = "first";
this.lastname = "last";
}
public CustomerInfo(String firstname, String lastname)
{
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname(){
return firstname;
}
public void setFirstname(String firstname){
this.firstname = firstname;
}
public String getLastname(){
return lastname;
}
public void getLastname(String lastname){
this.lastname = lastname;
}
}
And finally my POST request:
{"CustomerInfo":{"firstname":"xyz","lastname":"XYZ"}}
with Content-Type specified to be application/json
However, when I print out the object value, the default value("first" and "last") got printed out instead of the value I passed in("xyz" and "XYZ")
Does anyone know why I am not getting the result I expected?
FIX
So it turned out that, the value of request body is not passed in because I need to have the #RequestBody annotation not only in my interface, but in the actual method implementation. Once I have that, the problem is solved.

So it turned out that, the value of request body is not passed in because I need to have the #RequestBody annotation not only in my interface, but in the actual method implementation. Once I have that, the problem is solved.

You can do it in many ways, Here i am going to do it in below different ways-
NOTE: request data shuld be {"customerInfo":{"firstname":"xyz","lastname":"XYZ"}}
1st way We can bind above data to the map as below
#RequestMapping(value = "/send", headers = "Accept=application/json", method = RequestMethod.POST)
public void sendEmails(#RequestBody HashMap<String, HashMap<String, String>> requestData) {
HashMap<String, String> customerInfo = requestData.get("customerInfo");
String firstname = customerInfo.get("firstname");
String lastname = customerInfo.get("lastname");
//TODO now do whatever you want to do.
}
2nd way we can bind it directly to pojo
step 1 create dto class UserInfo.java
public class UserInfo {
private CustomerInfo customerInfo1;
public CustomerInfo getCustomerInfo1() {
return customerInfo1;
}
public void setCustomerInfo1(CustomerInfo customerInfo1) {
this.customerInfo1 = customerInfo1;
}
}
step 1. create another dto classCustomerInfo.java
class CustomerInfo {
private String firstname;
private String lastname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
step 3 bind request body data to pojo
#RequestMapping(value = "/send", headers = "Accept=application/json", method = RequestMethod.POST)
public void sendEmails(#RequestBody UserInfo userInfo) {
//TODO now do whatever want to do with dto object
}
I hope it will be help you out. Thanks

The formatting on this is terrible, but this should work for jackson configuration.
<!-- Use Jackson for JSON conversion (POJO to JSON outbound). -->
<bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<!-- Use JSON conversion for messages -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
ALso, as mentioned in a comment, your JSON is wrong for your object.
{"firstname":"xyz",‌​"lastname":"XYZ"}
does appear to be the correct JSON for your object.

Sample Data :
[
{
"targetObj":{
"userId":1,
"userName":"Devendra"
}
},
{
"targetObj":{
"userId":2,
"userName":"Ibrahim"
}
},
{
"targetObj":{
"userId":3,
"userName":"Suraj"
}
}
]
For above data this pring controller method working for me:
#RequestMapping(value="/saveWorkflowUser", method = RequestMethod.POST)
public void saveWorkflowUser (#RequestBody List<HashMap<String ,HashMap<String ,
String>>> userList ) {
System.out.println(" in saveWorkflowUser : "+userList);
//TODO now do whatever you want to do.
}

remove those two statements from default constructor and try

Related

JSON String to Java object with JSON string key rename [duplicate]

This question already has answers here:
Different names of JSON property during serialization and deserialization
(14 answers)
Closed 1 year ago.
I have the following requirement for JSON string conversion to Java Object.
class Person {
private String firstName;
private String lastName;
}
ObjectMapper MAPPER = new ObjectMapper();
String jsonString = "{\"FST_NME\":\"stack\",\"LST_NME\":\"OVERFLOW\"}";
Person person = MAPPER.readValue(jsonString, Person.class);
The above conversion returns null as the Person class attribute name doesn't match.
With #JsonProperty it converts correctly, but the final JSON result key is the same key as in jsonString.
{
"FST_NME" : "stack",
"LST_NME" : "overflow"
}
but I am looking for something like below.
{
"firstName" : "stack",
"lastName" : "overflow"
}
I tried renaming the key in jsonString and it works as expected.
But can we achieve the above result using any annotations or any other approach?
Thanks.
You just need to add #JsonProperty in both setter and getters.
In your case,
You are reading JSON string key FST_NME, so you need to add #JsonProperty('FST_NME') in the setter method for firstName and as you want to get the final JSON string with key firstName so you need to add #JsonProperty('firstName') in the getter method of firstName.
And same for lastName.
Following is the working code.
package com.ubaid.stackoverflow;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
#Slf4j
public class Saravanan {
#SneakyThrows
public static void main(String[] args) {
ObjectMapper MAPPER = new ObjectMapper();
String jsonString = "{\"FST_NME\":\"stack\",\"LST_NME\":\"OVERFLOW\"}";
Person person = MAPPER.readValue(jsonString, Person.class);
String finalJson = MAPPER.writeValueAsString(person);
log.debug("Final JSON: {}", finalJson);
}
}
class Person {
private String firstName;
private String lastName;
#JsonProperty("firstName")
public String getFirstName() {
return firstName;
}
#JsonProperty("FST_NME")
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#JsonProperty("lastName")
public String getLastName() {
return lastName;
}
#JsonProperty("LST_NME")
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
The output of above code is:
Final JSON: {"firstName":"stack","lastName":"OVERFLOW"}
Add below annotation on gets methods.
#JsonGetter("FST_NME")
public String getFirstName(){
return first Name;
}
Read data to a DTO class (Person DTO) with #JsonProperty
Class Person as you wish
Convert DTO to Person
class PersonDTO {
#JsonProperty(value = "FST_NME")
private String firstName;
#JsonProperty(value = "LST_NME")
private String lastName;
}
class Person {
private String firstName;
private String lastName;
}
ObjectMapper MAPPER = new ObjectMapper();
String jsonString = "{\"FST_NME\":\"stack\",\"LST_NME\":\"OVERFLOW\"}";
PersonDTO persondto = MAPPER.readValue(jsonString, PersonDTO.class);
Person person = new Person();
person.setFirstName(persondto.getFirstName());
person.setLastName(persondto.getLastName());

Choosing what pojo to use based on the user role on an edit request

So I am wondering what the best way to process an edit request based on a user role.
Say I have the following PostMapping:
#PostMapping(value = "/edit")
public ResponseEntity<String> editIoc(#RequestBody GeneralPojoAllFields editRequest)
the GeneralPojoAllFields looks like this:
public class GeneralPojoAllFields {
private String firstName;
private String lastName;
private String onlyAdminCanEditField;
}
This is the pojo the the admin will be able to use and that will eventually get mapped into the entity class to be saved to the database. However, if we have a regular user who wants to edit it and hypothetically they aren't restricted in the UI would that design work? What I am currently thinking is I would have a user pojo like so:
public class UserPojo {
private String firstName;
private String lastName;
}
After the request mapping comes we check if the user is either regular user or an admin. If it is a regular user we just map the GeneralPojoAllFields to the UserPojo and it wont map over the onlyAdminCanEditField and continue from there.
Is there a better way to do this?
First, your backend should be as independent of the UI as possible. So, access control in UI is a good to have design, but you should not depend upon it.
Now, coming back to your question, yes you can use SecurityContextHolder to find out if the user if regular user/admin. However, if its possible, I would suggest making two controllers, one for admin and one for regular user. Use #PreAuthorize on the admin controller to restrict access. Having two separate controllers will increase readability of your code tremendously.
Additionally, you can call the same service class method from both the controllers. And since you already have two POJO classes, you can use them in #RequestBody and let Spring take care of the mappings for you.
Well, it depends what you think a better way would be. It also depends a bit on your data source. But as there is no information on that here, I would suggest that a better way to do yours is by inheritance.
Make UserPojo the super class and GeneralPojoAllFields extend that class.
UserPojo.java:
public class UserPojo {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public UserPojo() {}
public UserPojo(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
GeneralPojoAllFields.java:
public class GeneralPojoAllFields extends UserPojo {
private String onlyAdminCanEditField;
public String getOnlyAdminCanEditField() {
return onlyAdminCanEditField;
}
public void setOnlyAdminCanEditField(String onlyAdminCanEditField) {
this.onlyAdminCanEditField = onlyAdminCanEditField;
}
public GeneralPojoAllFields() {}
public GeneralPojoAllFields(String firstName, String lastName, String onlyAdminCanEditField) {
super(firstName, lastName);
this.onlyAdminCanEditField = onlyAdminCanEditField;
}
}
App.java:
public class App {
public static void main(String[] args) {
UserPojo up1 = new UserPojo();
up1.setFirstName("MyFirstName");
up1.setLastName("MyLastName");
GeneralPojoAllFields gpaf1 = new GeneralPojoAllFields();
gpaf1.setFirstName("MyFirstName");
gpaf1.setLastName("MyLastName");
gpaf1.setOnlyAdminCanEditField("yes");
}
}

Why #FormParam does not support content=type=application/json?

I have tested a Rest API using postman and while testing on selection raw option, it shows unsupported media for application/json header content type, but only working with url-encoded form. Please specify how to use #FormParam for application/json content type. Thanks in advance.
The #FormParam annotation is only used to access parameters passed in a normal form that usually uses x-www-form-urlencoded. If you're sending your data as a JSON in request body, you can just create a POJO class corresponding to your request body containing all properties of your JSON request and use it as the parameter of your REST method. The JAX-RS container automatically deserializes the JSON body of the request into the given object for you.
As an example, if your request body is something like this:
{
"firstName" : "Foo",
"lastName" : "bar"
}
Then you can define a POJO representing your request data as below:
public class PersonRequest {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
And define your REST endpoint to access this data as:
#POST
#Path("/persons")
#Consumes("application/json")
public String savePerson(PersonRequest personRequestData) {
// request data are deserialized into the personRequestDataas properties
System.out.println(personRequestData.getFirstName());
}
You don't need to specify any sort of annotation on your POJO class.

Show validation errors on webpage with spring-mvc?

The following controller serves a simple html page showing all persons in a repository.
Problem: I'm using validation constraints on the get-query. And if the query was invalid (in my example: lastname parameter is missing), then spring automatically throws an exception as response to the browser.
But I'd still want to render the persons.html page, just showing the errors indead of the repository content.
Question: how could I achieve this? Because if the validation fails, the method below is not even accessed.
#Controller
#RequestMapping("/persons")
public class PersonController {
#GetMapping //note: GET, not POST
public String persons(Model model, #Valid PersonForm form) {
//on the persons.html page I want to show validation errors
model.addAttribute("persons", dao.findAll());
return "persons";
}
}
public class PersonForm {
private String firstname;
#NotBlank
private String lastname;
}
Sidenote: I'm using thymeleaf as templating engine. But the same question would apply to jsp or jsf engine.
Adding BindingResult should solve this problem as #obecker pointed. I saw your remark, it works for GetMapping and #PostMapping as well.
Please check this out:
#SpringBootApplication
public class So45616063Application {
public static void main(String[] args) {
SpringApplication.run(So45616063Application.class, args);
}
public static class PersonForm {
private String firstname;
#NotBlank
private String lastname;
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
#Override
public String toString() {
return firstname + " " + lastname;
}
}
#RestController
#RequestMapping("/")
public static class Home {
#GetMapping
public void get(#Valid PersonForm form, BindingResult bindingResult) {
System.out.println(form);
System.out.println(bindingResult);
}
}
}
Call:
curl -XGET 'localhost:8080?firstname=f&lastname=l'
Will produce output:
f l
org.springframework.validation.BeanPropertyBindingResult: 0 errors
Call:
curl -XGET 'localhost:8080?firstname=f'
Will produce:
f null
org.springframework.validation.BeanPropertyBindingResult: 1 errors
You need an additional BindingResult bindingResult parameter in the persons method. You can use this bindingResult to see whether there are validation errors.
Spring has a nice guide that shows how to do this.
See https://spring.io/guides/gs/validating-form-input/

How to create DTO class

I want to create DTO class for User. my input to program is
firstname, lastname,lastname.role,group1,group2,group3.
so for each user role consist of group_1,group_2,group_3.....
In database i want to store in following format
demo,demo,demo,roleId, gorup_1_name group_1_Id
demo,demo,demo,roleId, gorup_2 and group_2_Id
demo,demo,demo,roleId, gorup_3 and group_3_Id
I was able separate all this things , but i want to assign this value to userDTO class and stored into database. basically im new to core java part. so how can create structure for this?
A Data Transfer Object (DTO) class is a java-bean like artifact that holds the data that you want to share between layer in your SW architecture.
For your usecase, it should look more or less like this:
public class UserDTO {
String firstName;
String lastName;
List<String> groups;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List<String> getGroups() {
return groups;
}
public void setGroups(List<String> groups) {
this.groups = groups;
}
// Depending on your needs, you could opt for finer-grained access to the group list
}
One thing to add:
The essence of a DTO is that it transfers data across the wire. So it will need to be Serializable.
http://martinfowler.com/eaaCatalog/dataTransferObject.html

Categories

Resources