How to ignore specific fields on income in Spring boot? - spring

I have my domain class as follows
#Getter
#Setter
public class Student {
private Long id;
private String firstName;
private String lastName;
}
And I have this controller
#RestController
#RequestMapping("/student")
public class StudentController {
#PostMapping(consumes = "application/json", produces = "application/json")
public ResponseEntity<Student> post(#RequestBody Student student) {
//todo save student info in db, it get's an auto-generated id
return new ResponseEntity<>(student, HttpStatus.CREATED);
}
}
Now what I want is to configure serializer in a way that it ignores the id field on income, so I get only firstName and lastName, but serialize it when I'm returning the object to the caller.

Its easy to use it with jackson. There is an annotation named #JsonProperty(access = Access.READ_ONLY) where you can define if the property should be de- or serialized. Just put that annotation on your id field.
#JsonProperty(access = Access.READ_ONLY)
private Long id;
The Controller:
#PostMapping(consumes = "application/json", produces = "application/json")
public ResponseEntity<Student> post(#RequestBody Student student) {
//here we will see the that id is not deserialized
System.out.println(student.toString());
//here we set a new Id to the student.
student.setId(123L);
//in the response we will see that student will serialized with an id.
return new ResponseEntity<>(student, HttpStatus.CREATED);
}
Requestbody:
{
"id":1,
"firstName": "Patrick",
"lastName" : "secret"
}
Output of toString():
Student [id=null, firstName=Patrick, lastName=secret]
Response:
{
"id": 123,
"firstName": "Patrick",
"lastName": "secret"
}
P.S. It will also work if you dont send an id property:
{
"firstName": "Patrick",
"lastName" : "secret"
}

Related

added data to the custom Dto class

also I have a user dto class
class UserDto{
Long id;
String name;
String email;
String password
}
when user login is success, I wants to show response like this
{
"user":{
"id":"1",
"name":"imesh"
},
"token":{
"access_token":{
"token":"bhacvyusbi",
"expired_at":"20200210"
},
"refresh_token":{
"token":"bhacvyusbi",
"expired_at":"20200210"
}
}
}
so I have created a Response dto
class ResponseDto{
UserDto user;
AllTokenDto reponse;
}
class AllTokenDto{
TokenDto access_token;
TokenDto refresh_token;
}
class TokenDto{
String token;
Date expiredDate;
}
In here userData is which I am get the data from database
ResponseDto responseDto = new ResponseDto();
responseDto.setUser(modelMapper.map(**userData**, UserDto.class));
I have successfully added ResponseDto to UserDto data,
how to added tokens data to the ResponseDto.

One to Many save (POST) results in Bad Request (400) using Fetch for List<Entity>

I am trying to save Parent (One) and Children (Many) entities at the same time.
I took help from here and here.
I have an User Entity like below:
#Entity
#Table(name = "app_user")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class AppUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "salutation")
private String salutation;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
#Column(name = "preference")
private String preference;
public AppUser(String salutation, String name, String email, String preference, List<Address> addressList,
List<Expertise> expertise) {
super();
this.salutation = salutation;
this.name = name;
this.email = email;
this.preference = preference;
this.addressList = addressList;
this.expertise = expertise;
}
#OneToMany(orphanRemoval = true, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "address_id")
private List<Address> addressList = new ArrayList<>();
#OneToMany(orphanRemoval = true, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "expertise_id")
private List<Expertise> expertise = new ArrayList<>();
My POST controller method.
#PostMapping("/appUsers")
public ResponseEntity<AppUser> createUser(#RequestBody AppUser appUser) {
try {
AppUser _appUser = appUserRepository.save(
new AppUser(appUser.getSalutation(), appUser.getName(), appUser.getEmail(),
appUser.getPreference(), appUser.getAddressList(),
appUser.getExpertise()));
return new ResponseEntity<>(_appUser, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
My pure JS (Fetch) snippet:
<script>
async function postDataToServer(postData) {
const baseURL = "http://localhost:8080/api";
try {
const res = await fetch(`${baseURL}/appUsers`, {
method: "post",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(postData),
});
if (!res.ok) {
const message = `An error has occured: ${res.status} - ${res.statusText}`;
throw new Error(message);
}
} catch (err) {
alert(err.message);
}
}
</script>
Using above, I can see the form data nicely forming up like below:
{
"salutation": "Mr.",
"name": "Ajay Kumar",
"email": "ajay#kumar.com",
"address_main": "1234 StreetName State 12345",
"address_1": "2345 StreetName State 23456",
"address_2": "3456 StreetName State 34567",
"preference": "Vegeterian",
"expertise": [
"java",
"springboot",
"javascript"
],
"secret": "1abc1234-1abc-4321-1234-1234abcd1234"
}
During submit if I don't select expertise, it all works find. i.e. the user gets saved but if I select expertise checkboxes I get a 400 bad request message at the browser console and JSON parse erroSTS console like this:
2022-02-25 11:02:53.009 WARN 25007 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of com.spring.boot.rocks.model.Expertise (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('java'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.spring.boot.rocks.model.Expertise (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('java') at [Source: (PushbackInputStream); line: 1, column: 234] (through reference chain: com.spring.boot.rocks.model.AppUser["expertise"]->java.util.ArrayList[0])]
I created a github project here if any more details are required.
Question: What I am missing? How do I convert expertise collection to List using pure JS only ? Or how do I handle expertise collection in controller?
Your form data is not in correct format. This should be like this:
{
"salutation": "Mr.",
"name": "Ajay Kumar",
"email": "ajay#kumar.com",
"address_main": "1234 StreetName State 12345",
"address_1": "2345 StreetName State 23456",
"address_2": "3456 StreetName State 34567",
"preference": "Vegeterian",
"expertise": [
{
"java",
"springboot",
"javascript"
}
],
"secret": "1abc1234-1abc-4321-1234-1234abcd1234"
}
Expertise and address in your parent class are lists, not normal objectType entity. If any of these two lists are not present, try to set them as emptyList before saving.

Spring rest controller giving unsupported content type

Hello all here is what i have:
StockController.java
#RestController
public class StockController {
#Autowired
private StockRepository repository;
#RequestMapping(value = "stockmanagement/stock")
public ResponseEntity<?> addStock(#RequestBody String stock
) {
System.out.println(stock);
return new ResponseEntity<>(HttpStatus.OK);
}
when I make a request like so using chrome advanced rest extension :
Raw Headers
Content-Type: application/json
Raw Payload
{"stock": {"productId": 2, "expiryAndQuantity" : {}, "id": 0}}
It works fine in that out comes a string of json
However when i try to replace String stock with Stock stock where stock looks like this:
public class Stock {
#Id
private String id;
private String productId;
private Map<LocalDateTime, Integer> expiryAndQuantity;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public Map<LocalDateTime, Integer> getExpiryAndQuantity() {
return expiryAndQuantity;
}
public void setExpiryAndQuantity(Map<LocalDateTime, Integer> expiryAndQuantity) {
this.expiryAndQuantity = expiryAndQuantity;
}
#Override
public String toString() {
return String.format(
""
);
}
}
I get an error where by the following is fed back to me:
"status": 415
"error": "Unsupported Media Type"
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException"
"message": "Content type 'application/json;charset=UTF-8' not supported"
"path": "/stockmanagement/stock"
My question is; how do i create a request which maps to my Stock object.
You can try with #JsonRootName annotation, by default Spring serialize using no root name value. like this:
{"productId": 2, "expiryAndQuantity" : {}, "id": 0}
But if you want that your serialization has a rootname you need to use #JsonRootName annotation.
#JsonRootName(value = "Stock")
And it'll produce something like this
{"Stock": {"productId": 2, "expiryAndQuantity" : {}, "id": 0}}
You can see more here
http://www.baeldung.com/jackson-annotations
instead of accepting a String Accept a Stock object.and accept it from a post request than having a get request
#RequestMapping(value = "stockmanagement/stock",method=RequestMethod.POST)
public ResponseEntity<?> addStock(#RequestBody Stock stock){
}
and your request should be sent like this
{
"productId": 2
,"expiryAndQuantity" : null
,"id": 0
}
all parameter names should be equal to the objects filed names,since spring has jackson binders on class path and object will be created inside the controller method. if you are planning on passing different parameters from the post request you can use
#JsonProperty("pid")
private String productId;
on the field name.

receiving json and deserializing as List of object at spring mvc controller

My code is as below:
controller
#RequestMapping(value="/setTest", method=RequestMethod.POST, consumes="application/json")
public #ResponseBody ModelMap setTest(#RequestBody List<TestS> refunds, ModelMap map) {
for(TestS r : refunds) {
System.out.println(r.getName());
}
// other codes
}
TestS pojo
public class TestS implements Serializable {
private String name;
private String age;
//getter setter
}
Json request
var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
$.ajax({
url: "/setTest",
data: z,
type: "POST",
dataType:"json",
contentType:'application/json'
});
It's giving this error
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.air.cidb.entities.TestS
I am using spring 3.1.2 and jackson 2.0.4
Edit: I want to receive list of TestS objects at my controller method, and process them. I am not able to find if I am sending wrong json or my method signature is wrong.
Here is the code that works for me. The key is that you need a wrapper class.
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
A PersonWrapper class
public class PersonWrapper {
private List<Person> persons;
/**
* #return the persons
*/
public List<Person> getPersons() {
return persons;
}
/**
* #param persons the persons to set
*/
public void setPersons(List<Person> persons) {
this.persons = persons;
}
}
My Controller methods
#RequestMapping(value="person", method=RequestMethod.POST,consumes="application/json",produces="application/json")
#ResponseBody
public List<String> savePerson(#RequestBody PersonWrapper wrapper) {
List<String> response = new ArrayList<String>();
for (Person person: wrapper.getPersons()){
personService.save(person);
response.add("Saved person: " + person.toString());
}
return response;
}
The request sent is json in POST
{"persons":[{"name":"shail1","age":"2"},{"name":"shail2","age":"3"}]}
And the response is
["Saved person: Person [name=shail1, age=2]","Saved person: Person [name=shail2, age=3]"]
This is not possible the way you are trying it. The Jackson unmarshalling works on the compiled java code after type erasure. So your
public #ResponseBody ModelMap setTest(#RequestBody List<TestS> refunds, ModelMap map)
is really only
public #ResponseBody ModelMap setTest(#RequestBody List refunds, ModelMap map)
(no generics in the list arg).
The default type Jackson creates when unmarshalling a List is a LinkedHashMap.
As mentioned by #Saint you can circumvent this by creating your own type for the list like so:
class TestSList extends ArrayList<TestS> { }
and then modifying your controller signature to
public #ResponseBody ModelMap setTest(#RequestBody TestSList refunds, ModelMap map) {
#RequestMapping(
value="person",
method=RequestMethod.POST,
consumes="application/json",
produces="application/json")
#ResponseBody
public List<String> savePerson(#RequestBody Person[] personArray) {
List<String> response = new ArrayList<String>();
for (Person person: personArray) {
personService.save(person);
response.add("Saved person: " + person.toString());
}
return response;
}
We can use Array as shown above.
Solution works very well,
public List<String> savePerson(#RequestBody Person[] personArray)
For this signature you can pass Person array from postman like
[
{
"empId": "10001",
"tier": "Single",
"someting": 6,
"anything": 0,
"frequency": "Quaterly"
}, {
"empId": "10001",
"tier": "Single",
"someting": 6,
"anything": 0,
"frequency": "Quaterly"
}
]
Don't forget to add consumes tag:
#RequestMapping(value = "/getEmployeeList", method = RequestMethod.POST, consumes="application/json", produces = "application/json")
public List<Employee> getEmployeeDataList(#RequestBody Employee[] employeearray) { ... }
I believe this will solve the issue
var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
z = JSON.stringify(JSON.parse(z));
$.ajax({
url: "/setTest",
data: z,
type: "POST",
dataType:"json",
contentType:'application/json'
});
For me below code worked, first sending json string with proper headers
$.ajax({
type: "POST",
url : 'save',
data : JSON.stringify(valObject),
contentType:"application/json; charset=utf-8",
dataType:"json",
success : function(resp){
console.log(resp);
},
error : function(resp){
console.log(resp);
}
});
And then on Spring side -
#RequestMapping(value = "/save",
method = RequestMethod.POST,
consumes="application/json")
public #ResponseBody String save(#RequestBody ArrayList<KeyValue> keyValList) {
//Saving call goes here
return "";
}
Here KeyValue is simple pojo that corresponds to your JSON structure also you can add produces as you wish, I am simply returning string.
My json object is like this -
[{"storedKey":"vc","storedValue":"1","clientId":"1","locationId":"1"},
{"storedKey":"vr","storedValue":"","clientId":"1","locationId":"1"}]

GSON,AndroidAnnotations - Expected BEGIN_OBJECT but was String

I searched similar topics but none of them helped me.
My JSON response is:
{
"success": "true",
"data": {
"id": "x",
"user_name": "xxx",
"email": "xxx#xxx.com",
"first_name": "xxx",
"last_name": "xx",
"position": "xxx",
"session_id": "xxx"
}
}
My Java classes are:
Response:
public class Response {
public String success;
public Data data;
public Response() {
}
public Response(String success, Data data) {
this.success = success;
this.data = data;
}
}
Data
public class Data {
public String id;
public String user_name;
public String email;
public String first_name;
public String last_name;
public String position;
public String session_id;
public Data() {
}
public Data(String id, String user_name, String email, String first_name, String last_name, String position, String session_id) {
this.id = id;
this.user_name = user_name;
this.email = email;
this.first_name = first_name;
this.last_name = last_name;
this.position = position;
this.session_id = session_id;
}
}
I am using android annotations to establish rest connection.
My RestClient is:
#Rest(rootUrl = "http://xxx/services", converters = {GsonHttpMessageConverter.class})
public interface MyRestClient {
#Post("/login.php")
ResponseEntity<Response> login(User user);
RestTemplate getRestTemplate();
void setRestTemplate(RestTemplate restTemplate);
}
And in main activity I use:
ResponseEntity<Response> resp = restCli.login(new User("xxx","xxx"));
I get an error
Expected BEGIN_OBJECT but was String at line 1 column 4
I tried to change 'success' filed type to boolean,Boolean i Java class - didn't help.
I tried changing the method return type in the rest interface to void and then no error, so I think the error is connected with wrong response class, but I have no idea what is wrong. Could you help me?

Resources