How to Use #JsonProperty With Lombok? - java

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;
}

Related

Which annotations should I use in request & response class in Java?

I have been confused due to the different usages and annotations for request & response class in Java. Some of them use #Value, some others #Data. Similarly some of them #RequiredArgsConstructor, some others #AllArgsConstructor. So, could you pleae post a suitable request and response classes (for example 'EmployeeRequestandEmployeeDTO`) for a general Java convention?
Request:
#Data
public class BrandRequest {
#NotEmpty
private String name;
private UUID cityUuid;
}
Response (DTO):
#Data
#NoArgsConstructor
public class BrandDTO {
private UUID uuid;
private String name;
public BrandDTO(final Brand brand) {
this.uuid = brand.getUuid();
this.name = brand.getName();
}
}

How to tell MapStruct "not" to use the Lombok Builder?

I have the following classes and mapper to map them.
How can I configure Mapstruct to "not" use the Lombok builder? (without removing the #Builder annotation)?
When using the latest version of Lombok and mapstruct, mapstruct will automatically use the Builder when the #Builder annotation is used. I can not find a way to disable that, as I need the Instance in an #AfterMapping method as the builder doesn't expose all required methods (#SuperBuilder is not allowed in this use case)
#Entity(name = "user_details")
#Data
#Builder
public class User extends AuditableEntityBase {
#Version
#NotNull
private Integer version;
#NotNull
private String name;
#NotNull
private String email;
#NotNull
private Address address; // Just another Class containing another class that is mapped as well.
}
#Value
#Builder
public class UserDto extends AuditableEntityBaseDto {
#NotNull
private Integer version;
#NotNull
private String name;
#NotNull
private String email;
#NotNull
private AddressDto address;
}
#Mapper(componentModel = "spring")
class UserRestMapper {
public abstract UserDto map(User obj);
}
#AfterMapping
public void decorate(User source, #MappingTarget AuditableEntityBase target) {
// Method is never called.
// Method is called in case the second argument is: "#MappingTarget UserDto.UserDtoBuilder target"
}
If you want to disable using builders you can do so by adding #Builder(disableBuilder = true) to your #Mapper.
e.g.
#Mapper(componentModel = "spring", builder = #Builder(disableBuilder = true))
class UserRestMapper {
public abstract UserDto map(User obj);
}
Note #Builder is from the org.mapstruct package
No idea about disabling it, but why not do it like this?
#Mapper(componentModel = "spring")
abstract class UserRestMapper {
public abstract UserDto map(User obj);
public UserDto decoratedMap(User obj) {
UserDto mapped = map(obj);
// your after mapping logic
return mapped;
}
}

Jackson Ignores Parent Class properties sent via JSON

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

Jackson #JsonIgnoreProperties isn't working on referenced class property?

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?

How do I use lombok #Builder to store different values?

I have the following JPA entity
#Data
#Builder
public class Post {
#Id
#GeneratedValue
UUID id;
#OneToMany
Set<PostTags> tags;
String content;
}
#Data
public class PostTag {
#Id
#GeneratedValue
UUID id;
#OneToOne
Post post;
String tag;
}
Using lombok #Builder I want to be able to do the following
Post post = Post.builder()
.tags("hello", "world")
.content("Hello world")
.build();
I am presuming I need a custom builder along the lines of
public static class PostBuilder {
private Set<String> myTags = new HashSet<>();
public PostBuilder tags(String... tags) {
myTags.addAll(Arrays.asList(tags));
return this;
}
}
From the documentation it appears there ObtainVia annotation that I can use, but I am not sure how to get around it (no example on the doc) and especially since I only want myTags to be a builder specific thing, and not be exposed on the main class itself.
ObtainVia only works for toBuilder, so that won't help much in this case.
I suggest the following approach.
First, add a factory method in PostTag, e.g. createTag(String). This method only sets tag in the instance it creates and leaves everything else null. Statically import it into the class where you want to use PostBuilder.
Next, use #Singular on tags. Then you can write:
Post post = Post.builder()
.tag(createTag("hello"))
.tag(createTag("world"))
.content("Hello world")
.build();
Finally, customize the build() method so that it first creates the Post instance (like an uncustomized build() method would) and then sets this newly created Post instance as post in all PostTag instances.
Have a look at the delomboked code to make sure you use the right builder class and method headers when customizing the builder.
You can use #Accessors for what you're asking:
Post
#Data
#Accessors(chain = true)
public class Post {
#Id
#GeneratedValue
private UUID id;
#OneToMany
private Set<PostTags> tags;
private String content;
public Post tags(String... tags) {
Arrays.stream(tags)
.map(tag -> PostTags.builder().tag(tag).build())
.forEach(this.tags::add);
return this;
}
}
PostTags
#Data
#Builder
public class PostTags {
#Id
#GeneratedValue
private UUID id;
#OneToOne
private Post post;
private String tag;
}
When you using #Accessors(chain = true), The setters will return this reference instead of void, and then your code will act this way:
Post post = new Post().setId(id).tags("aaa", "bbb");
If you want your code to be more similar to builder then add fluent value to the annotation: #Accessors(chain = true, fluent = true)
It will remove the set<Something> from the setters and just use the name of the fields, and then your code will look like this:
Post post = new Post().id(id).content("hello").tags("aaa", "bbb");

Categories

Resources