I am trying to make a model (Request) class that would ignore a nested class's property it references. And I was told that I cannot modify referenced model (User) in any way, so what I had tried was to ignore its property from the Request model.
#Data
#Builder
public class Request {
#JsonIgnoreProperties(value = {"id"})
User user;
}
// class I cannot modify
#Data
#Builder
public class User {
String id;
String name;
...
}
In the payload, I am still seeing id serialized & deserialized so the annotation clearly doesn't work. It seems to work fine if I place it at the class level of User but since I cannot modify User, I've also tried:
#JsonIgnoreProperties(value = {"user.id"})
public class Request {
User user;
None of above works. The doc is saying
Starting with 2.0, this annotation can be applied both to classes and to properties
I am using Jackson 2.10.2 in a Spring Boot project. What am I missing?
Create a class who extends User as bellow:
public class MyUser extends User {
#JsonIgnore
String id;
}
#Data
#Builder
public class Request {
MyUser user;
}
I'm running this test and works:
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Request {
#JsonIgnoreProperties("id")
User user;
}
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class User {
String id;
String name;
}
public class TestClass {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper= new ObjectMapper();
final Request value = Request.builder()
.user(User.builder()
.id("qw423432")
.name("asdasdjsadjasdasd")
.build())
.build();
System.out.println(mapper.writeValueAsString(value));
System.out.println(mapper.readValue("{\"user\":{\"id\": \"a\", \"name\":\"b\"}}", Request.class));
}
}
Do you see any difference with your test?
Related
Suppose we have a json response that we want to map it to our java class.
{
"access_token": "abcdefg..."
}
I had a data class that mapped access_token field in the json to accessToken field in the code. I used to use #JsonProperty annotation on getters and setters.
private String accessToken;
#JsonProperty("accessToken")
public String getAccessToken() {
return accessToken;
}
#JsonProperty("access_token")
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
Then I decided to use Lombok annotations #Getter and #Setter. Since I do not have a getter and setter in my code, how can I map access_token field in the json to accessToken field in the code with Lombok annotations?
My code is like this right now and as you can expect, it cannot map the fields.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Token {
private String accessToken;
}
I do not want to name my variable to access_token because I also return access token as a json response and I want it to appear as accessToken in my json response.
Thank you.
Found a way to do it.
#NoArgsConstructor
#AllArgsConstructor
public class Token {
#Setter(onMethod = #__(#JsonSetter(value = "access_token")))
#Getter(onMethod = #__(#JsonGetter(value = "accessToken")))
private String accessToken;
}
Another solution is to use #JsonAlias annotation.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Token {
#JsonAlias("access_token")))
private String accessToken;
}
No need for all this, you can simply annotate your field with #JsonProperty. Spring will still use the getters for serialization.
#Data
public class Token {
#JsonProperty("access_token")
private String accessToken;
}
Assuming two simple classes:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Person extends Party {
private String name;
private Long sex;
}
The compilation fails on the following error. Upon reading Lombok | #SuperBuilder I have no idea what I could miss.
C:\Dev\companyproject\src\main\java\com\companyproject\entity\Person.java:12
java: type com.companyproject.entity.Party.PartyBuilder does not take parameters
The issue here is the incorrect #Builder annotation on the parent class. The documentation for #SuperBuilder mentions:
Most importantly, it requires that all superclasses also have the #SuperBuilder annotation.
So the correct parent class would be:
#Data
#SuperBuilder // <- annotation replaced here
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
Addendum:
A key difference between both annotations is that #SuperBuilder also creates a constructor of the class expecting a corresponding builder argument.
For Party it would look like:
protected Party(PartyBuilder<?, ?> b) {
this.id = b.id;
this.status = b.status;
}
and for Person:
protected Person(PersonBuilder<?, ?> b) {
super(b);
this.name = b.name;
this.sex = b.sex;
}
As you can see, the child class constructor wants to pass its own builder to the parent class constructor and this will only be possible if there is a matching constructor there, and #Builder wouldn't generate it.
Also PersonBuilder<> extends PartyBuilder<>, that is why calling super with the child type builder works fine here.
This is my entity:
#Entity
#Table
#NoArgsConstructor
#AllArgsConstructor
#Setter
#Getter
public class Sample {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private boolean whatever;
}
And I want to insert mock data to the db:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
Service service = context.getBean(Service.class);
service.save(new Sample("name", true); //Doesnt compile, lacks id
service.save(new Sample(10, "name", true); //If row with id 10 exists, it gets updated
}
}
As explained in the comments, either it doesn't compile or it replaces the row.
When adding data through the controller, I can just leave the id on the json undefined and it will generate a new one. How does the controller achieve this?
EDIT:
The service saves the object by calling the save method of the JpaRepository
public Sample save(Sample s) {
return repository.save(s);
}
Your problem os that your pojo Sample do not have a constructor without id. You've used the annotations #NoArgsConstructor and #AllArgsConstructor that gives you an empty constructor and a constructor with all the fields, respectvly. In the controller It works due to the serialization and deserialization present on the controller (Jackson by default on Spring) that is capable of building the object with missing fields, using the empty constructor and direct access to fields.
All you have to do is to create an constructor with the fields you need on your pojo Sample, so in the case described would be something like:
public Sample(String name, boolean whatever) {
this.name = name;
this.whatever = whatever;
}
This will fit your problem.
I am creating REST Service with spring boot and defined some classes with inheritence, however I'm not able to receive a JSON payload which I am sending from postman to the controller.
JSON Payload which I'm sending :
{
"dummy" : "okok",
"fullName": "okok",
"mobileNumber": 1234567890
}
I am only getting dummy property in the controller, rest of the properties not getting mapped to POJO.
Logging statement prints following line on the console
ownerAccount OwnerAccount(dummy=okok)
I think only OwnerAccount constructor is getting invoked and Account properties not getting initialized.
Please help me understand the missing part or mistake I am doing here.
I have defined following structure :
Account.java
#NoArgsConstructor
#AllArgsConstructor
#Data
public class Account {
#NotBlank(message = "fullName is mandatory")
private String fullName;
#NotNull(message = "mobileNumber is mandatory")
private Long mobileNumber;
#Valid
private AddressRequest addressRequest;
}
OwnerAccount.java
#NoArgsConstructor
#Data
public class OwnerAccount extends Account {
#NotBlank(message = "dummy is mandatory")
private String dummy;
}
OwnerController.java
#RestController
#RequestMapping(path = "/api/v1/account/owner")
public class OwnerAccountResource {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private OwnerAccountService ownerAccountService;
#Autowired
public OwnerAccountResource(OwnerAccountService ownerAccountService) {
this.ownerAccountService = ownerAccountService;
}
#PostMapping("/signup")
public ResponseEntity createOwnerAccount(#RequestBody #Valid OwnerAccount ownerAccountRequest) {
logger.info("ownerAccountDto {}", ownerAccountRequest);
return ResponseEntity.ok(ownerAccountService.createAccount(ownerAccountRequest));
}
}
I suppose you think you are not able to receive dummy field because here you are printing only Account fields because #Data add #ToString annotation but it's not printing super class fields
logger.info("ownerAccountDto {}", ownerAccountRequest);
but if you could debug that controller you would see dummy field is there. You need to override toString() to log dummy field or just add lombok annotation
#NoArgsConstructor
#Data
#ToString(callSuper = true)
public class OwnerAccount extends Account {
#NotBlank(message = "dummy is mandatory")
private String dummy;
}
#ToString(callSuper = true) will include fields from super class
JsonDeserialize not working when objectmapper readvalue for inherited properties.
Vehicle Class
#Getter
#Setter
#JsonDeserialize(builder = Vehicle.VehicleBuilder.class)
#Builder(builderClassName = "VehicleBuilder", toBuilder = true)
public class Vehicle{
private String name;
private String noOfTyres;
#JsonPOJOBuilder(withPrefix = "")
public static class VehicleBuilder{
}
}
Car class
#Getter
#Setter
#JsonDeserialize(builder = Car.CarBuilder.class)
#Builder(builderClassName = "CarBuilder", toBuilder = true)
public class Car extends Vehicle {
private String carType;
#JsonPOJOBuilder(withPrefix = "")
public static class CarBuilder extends VehicleBuilder {
}
}
I don't want to create #NoArgsConstructor ,#AllArgsConstructor in both classes.
My issue when Car car = om.readValue(jsonValue,Car.class);
When I parse Json to java object the parent class properties are not setting properly.
As of now I'm using #NoArgsConstructor ,#AllArgsConstructor for work around for the use case.
Is there any way to use it along with #JsonDeserialize and #JsonPOJOBuilder?
The problem with the code is that it assumes that builders in inherited classes will set the parent properties as well. Unfortunately, they don't do that out of the box. However, this is something that can be achieved with Lombok but requires some additional code, as described in this post.
A complete solution could look as follows.
Parent Class
#Getter
#Setter
#JsonDeserialize
#Builder(builderClassName = "VehicleBuilder", builderMethodName = "vehicleBuilder")
public class Vehicle {
private String name;
private String noOfTyres;
}
Child Class
#Getter
#Setter
#JsonDeserialize(builder = Car.CarBuilder.class)
public class Car extends Vehicle {
private String carType;
#Builder
public Car(String name, String noOfTyres, String carType) {
super(name, noOfTyres);
this.carType = carType;
}
#JsonPOJOBuilder(withPrefix = "")
public static class CarBuilder extends VehicleBuilder {
}
}
Notice that the builder on the extending class is achieved by supplying a constructor with the #Builder annotation. Also take notice that the extending class does not set annotation parameter toBuilder=true as that will require access to parent properties which are private. This can be achieved by setting parent class properties to protected.