Setter method ignored by Jackson - java

I have the following Java beans:
public class Customer implements Serializable {
private Integer id;
private String name;
private Country country;
public void setId(Integer id) {
this.id=id;
}
public void setName(String n) {
this.name=n;
}
public void setCountry(Country c) {
this.country=c;
}
public void setCountryId(Integer id) {
this.country= new Country();
this.country.setId(id)
}
//...getters here
}
and
public class Country {
private Integer id;
private String code; //es, us, fr...
private void setId(Integer id) {
this.id=id;
}
//rest of setters and getters
}
and I have the following method:
#RequestMapping(value = "/customer/", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Customer> addSecondaryCustomer(#RequestBody Customer sc) {
this.customerService.addCustomer(sc);
return new ResponseEntity<>(sc,HttpStatus.OK);
}
Using the Web Development tools I can see the server is receiving the following:
{
"name": "Pablo Test",
"countryId": 1
}
I can see that the field name is populated, but country remains null. I've tried to set a breakpoint in both setters, but none of them is being called, so it seems that the object mapper is looking for attributes, ignoring setters. ¿Why is this happening?
I am using Jackson 2.9.0 and Spring 4.2.13. It worked with older versions of Spring (4.2.0) and Jackson (2.1.4)
PS: I know I can workaround this by sending "country": { "id": 1} in my AJAX request, but I need to know what's happening here.

Related

Error invoque REST service method with various #BeanParam beans - "Unrecognized field"

I' have this scenario:
Bean class
public class BeanRequest {
#QueryParam("id")
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public class OtherBeanRequest {
#QueryParam("descr")
private String descr;
public Long getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
}
REST Service
#PUT
#Path("/update")
public Response update(#Valid #BeanParam BeanRequest request1, #BeanParam OtherBeanRequest request2 ) {
return Response.ok("OK").build();
}
Problem
When this method is invoqued from frontend (or GoogleREST tool), only parameters defined in variable "request1" is recognized.
The variable "request2" is ignored with the error "Unrecognized field (package.OtherBeanRequest) 'desc'".
Note 1: The test whit Arquillian work fine!
Note 2: DOC: https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/jaxrs-resources.html#d0e2545 denote that is possible make method with various #BeanParam beans.
My application server is Jboss 7.2
Thanks in advance.

Converting JSON to POJO and throw exception in case of null value for required field

I want to convert a JSON into POJO(i.e. class) and a class that has some field annotated with #Notnull. Converting that JSON into POJO, but if JSON has a null value for a required field in that case object mapper should throw an exception.
I do not know, what I am doing mistake in this, please help me.
Class:-
public class Abc {
#NotNull
private String id;
#NotNull
private String entityType;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
}
Public class Demo{
public static void main(String []args){
String data="{ \"supportEntitySpecification.id\":\"an001\", \"supportEntityType\":null }";
Abc abc= objectMapper.readValue(data),
Abc.class);
}
}
#NotNull is an annotation. Annotations do nothing on their own. See these as Documentation.
Your can use ConstraintValidator for example to valid your Object with annotations: ConstraintValidator

SpringBoot deserialization without default constructor

During the last hours I read many StackOverflow questions and articles, but none of the advices helped. What I tried:
Add #JsonCreator and #JsonProperty to both Person and Employee classes (link)
Add #JsonDeserialize(using = EmployeeDeserialize.class) to Employee class (link)
Add Lombok as dependency, set lombok.anyConstructor.addConstructorProperties=true and add #Data / #Value annotation to both Person and Employee classes (link)
Finally, I did the deserialization manually:
String json = "{\"name\": \"Unknown\",\"email\": \"please#work.now\",\"salary\":1}";
ObjectMapper objectMapper = new ObjectMapper();
Employee employee = objectMapper.readValue(json, Employee.class);
In this way I could deserialize the JSON, but as soon as I started my spring-boot-starter-web project and called
http://localhost:8080/print?name=unknown&email=please#work.now&salary=1
I got the good old BeanInstantiationException
Failed to instantiate [Employee]: No default constructor found
I run out of ideas. Does anybod know why this worked when I did the deserialization manually? And why it throws exception when I call the REST endpoint?
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
public class EmployeeController {
#GetMapping("print")
public void print(Employee employee) {
System.out.println(employee);
}
}
public class Person {
private final String name;
#JsonCreator
public Person(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Employee extends Person {
private final String email;
private final int salary;
#JsonCreator
public Employee(
#JsonProperty("name") String name,
#JsonProperty("email") String email,
#JsonProperty("salary") int salary) {
super(name);
this.email = email;
this.salary = salary;
}
public String getEmail() {
return email;
}
public int getSalary() {
return salary;
}
}
You’re implementing JSON deserialisation, yet you’re not using any JSON.
Change to use #PostMapping on your controller method and use something like Postman or cURL to send the JSON to your /print endpoint.

Jackson JSON Mapping JSON keys with JAVA BEAN

I'm using Jackson JSON LIB 2.8, and i'm using Json.mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true);
but sometimes I need to map some fields with two differents names like
POJO has attribute builindg and i need to map it to buildingUid or BUILDING depends on what key exist in JSON, are they any way to do this ?
Example :
public class Building extends Bean {
private UUID id;
private String name;
}
and I have two different sources , one is my Database which return a JSON with this format :
{
"ID":"build",
"NAME":"name1"
}
and my other source is a client :
{
"UID" : "build",
"name" : "name1"
}
As you can see my problem is to map id with both UID and ID , i manage to map the first one with :
Json.mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true);
but the other source is UID and i dont know how to map it automatically when i do something like
Json.encode(Building.toString());
It is possible using multiple setters annotated with #JsonSetter
Try something like this :
public class Building extends Bean {
private String id;
private String name;
#JsonGetter("id")
public String getId() {
return id;
}
#JsonSetter("id")
public void setId(String id) {
this.id = id;
}
#JsonSetter("UID")
public void setUID(String id) {
setId(id);
}
#JsonGetter("name")
public String getName() {
return name;
}
#JsonSetter("name")
public void setName(String name) {
this.name = name;
}
#JsonSetter("NAME")
public void setUpperCaseName(String name) {
setName(name);
}
}
But it's a little lousy (solution not dynamic).

Deserializing JSON wrapper object with list returns null properties

I got json like below:
{"examinationTypes":[{"ExaminationTypeVO":{"id":1,"name":"Badanie krwi"}},{"ExaminationTypeVO":{"id":2,"name":"Spirometria"}},{"ExaminationTypeVO":{"id":3,"name":"Wymaz"}},{"ExaminationTypeVO":{"id":4,"name":"Ciśnienie"}},{"ExaminationTypeVO":{"id":5,"name":"EKG"}},{"ExaminationTypeVO":{"id":6,"name":"Elektrowstrząsy"}},{"ExaminationTypeVO":{"id":7,"name":"Tomografia"}},{"ExaminationTypeVO":{"id":8,"name":"Lewatywa"}},{"ExaminationTypeVO":{"id":9,"name":"Aneskopia"}},{"ExaminationTypeVO":{"id":10,"name":"Rektoskopia"}},{"ExaminationTypeVO":{"id":11,"name":"Kolonoskopioa"}},{"ExaminationTypeVO":{"id":12,"name":"Echo serca"}},{"ExaminationTypeVO":{"id":13,"name":"Ablacja"}},{"ExaminationTypeVO":{"id":14,"name":"Badnaie dopplerowskie"}},{"ExaminationTypeVO":{"id":15,"name":"Kapilaroskopia"}}]}
I have defined types:
#JsonRootName(value="ExaminationTypeVO")
#JsonIgnoreProperties(ignoreUnknown = true)
public class ExaminationTypeVO {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and
public class ExaminationTypesVO {
private List<ExaminationTypeVO> examinationTypes;
public List<ExaminationTypeVO> getExaminationTypes() {
return examinationTypes;
}
public void setExaminationTypes(List<ExaminationTypeVO> examinationTypes) {
this.examinationTypes = examinationTypes;
}
When I am deserializing it like that:
ExaminationTypesVO l = m.readValue(result, ExaminationTypesVO.class);
I receive an wrapper object but the list inside contains objects of type ExaminationTypeVO with all properties set to null.
Can anybody help to figure it out?
Your issue is that you have an extra level of object that you are trying to deserialize. Trying to not be confusing as I explain this: you have an array of objects, those objects contain a single ExaminationTypeVO object.
If you are stuck with the structure of the JSON that you provided, then you will need to add another "level" to your deserialization. You can do this via a wrapper object inside of your ExaminationTypesVO class:
public class ExaminationTypesVO {
private List<ExaminationTypeVOWrapper> examinationTypes;
public List<ExaminationTypeVOWrapper> getExaminationTypes() {
return examinationTypes;
}
public void setExaminationTypes(List<ExaminationTypeVOWrapper> examinationTypes) {
this.examinationTypes = examinationTypes;
}
public static class ExaminationTypeVOWrapper {
private final ExaminationTypeVO examinationTypeVO;
#JsonCreator
public ExaminationTypeVOWrapper(#JsonProperty("ExaminationTypeVO") ExaminationTypeVO examinationTypeVO) {
this.examinationTypeVO = examinationTypeVO;
}
public ExaminationTypeVO getExaminationTypeVO() {
return examinationTypeVO;
}
}
}
If you have control over the JSON that you are deserializing, you can just remove the extra "level" (ExaminationTypeVO wrapping object) and not have to change your code. Your new JSON in this approach would look like:
{
"examinationTypes": [
{
"id": 1,
"name": "Badanie krwi"
}, ...
]
}
With either of these approaches you can remove both of the class-level annotations you have on ExaminationTypeVO.

Categories