The Jackson library's ObjectMapper is a core class for reading and writing JSON in Java applications. It's widely used in Spring Boot and other Java frameworks for serializing Java objects to JSON and deserializing JSON into Java objects.
@SpringBootTest
class RestApiApplicationTests {
@Autowired
private ObjectMapper objectMapper;
@Test
void contextLoads() throws JsonProcessingException {
var user = new UserRequest();
user.setUserName("Choi");
user.setUserAge(24);
user.setEmail("choi@gmail.com");
user.setIsKorean(true);
var json = objectMapper.writeValueAsString(user);
System.out.println(json);
//prints: {"user_name":"Choi","user_age":24,"email":"choi@gmail.com","is_korean":true}
var dto = objectMapper.readValue(json, UserRequest.class);
System.out.println(dto);
//prints: UserRequest(userName=Choi, userAge=24, email=choi@gmail.com, isKorean=true)
}
}
//@Data => Commented out to remove default getters/setters
@AllArgsConstructor
//@NoArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {
private String userName;
private Integer userAge;
private String email;
private Boolean isKorean; //default is false
}
@Test
void contextLoads() throws JsonProcessingException {
var user = new UserRequest("Choi", 24, "choi@gmail.com", true);
// user.setUserName("Choi");
// user.setUserAge(24);
// user.setEmail("choi@gmail.com");
// user.setIsKorean(true);
System.out.println(user);
var json = objectMapper.writeValueAsString(user);
System.out.println(json);
//var dto = objectMapper.readValue(json, UserRequest.class);
//System.out.println(dto);
}
/*printed:
com.example.rest_api.model.UserRequest@74a58a06 (from System.out.println(user); )
{} (from System.out.println(json);)
*/
//@Data
@AllArgsConstructor
//@NoArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {
private String userName;
private Integer userAge;
private String email;
private Boolean isKorean; //default is false
@Override
public String toString() {
return "UserRequest{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
", email='" + email + '\'' +
", isKorean=" + isKorean +
'}';
}
}
/*
prints:
UserRequest{userName='Choi', userAge=24, email='choi@gmail.com', isKorean=true}
*/
public String name(){
return this.userName;
}
public int humanAge(){
return this.userAge;
}
UserRequest{userName='Choi', userAge=24, email='choi@gmail.com', isKorean=true}
{}
public String getName(){
return this.userName;
}
public int getHumanAge(){
return this.userAge;
}
/* the output we get is:
{"name":"Choi","human_age":24}
*/
//@Data
@AllArgsConstructor
//@NoArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {
private String userName;
private Integer userAge;
private String email;
private Boolean isKorean; //default is false
public String getUserName() {
return userName;
}
public Integer getUserAge() {
return userAge;
}
public String getEmail() {
return email;
}
public Boolean getKorean() {
return isKorean;
}
// public String getName(){
// return this.userName;
// }
//
// public int getHumanAge(){
// return this.userAge;
// }
@Override
public String toString() {
return "UserRequest{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
", email='" + email + '\'' +
", isKorean=" + isKorean +
'}';
}
}
{"user_name":"Choi","user_age":24,"email":"choi@gmail.com","korean":true}
{"user_name":"Choi","user_age":24,"email":"choi@gmail.com","is_korean":true}
public String getUser(){
return userName;
}
/* output:
{"user_name":"Choi","user_age":24,"email":"choi@gmail.com","is_korean":true,"user":"Choi"}
*/
since objectMapper utilized getUser() class, converted User into snake_case, before adding it to the JSON object. If we want to keep getUser() method but we do not want our serialized JSON object to contain key "user" like above, we can add an annotation to the getUser() method, so that objectMapper ignores this get method while serializing.
@JsonIgnore
public String getUser(){
return this.userName;
}
Or if we want, for example, the variable "email" to be "user_email" in the JSON, we can add annotation to the member variable declaration as below:
@JsonProperty("user_email")
private String email;
/*
This causes the email variable to be named as "user_email" in the Json, printing below:
{"user_name":"Choi","user_age":24,"is_korean":true,"user_email":"choi@gmail.com"}
//@Data
//@AllArgsConstructor
//@NoArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {
private String userName;
private Integer userAge;
private String email;
private Boolean isKorean; //default is false
private UserRequest(){}
@Override
public String toString() {
return "UserRequest{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
", email='" + email + '\'' +
", isKorean=" + isKorean +
'}';
}
}
var json = "{\"user_name\":\"Choi\",\"user_age\":24,\"email\":\"choi@gmail.com\",\"is_korean\":true}";
System.out.println(json);
/*prints: {"user_name":"Choi","user_age":24,"email":"choi@gmail.com","is_korean":true}
*/
var dto = objectMapper.readValue(json, UserRequest.class);
System.out.println(dto);
/*
prints: UserRequest{userName='null', userAge=null, email='null', isKorean=null}
*/
@Setter
public class UserRequest{
//...
}
/*
Result:
UserRequest{userName='Choi', userAge=24, email='choi@gmail.com', isKorean=true}
*/
@JsonProperty("user_name")
private String userName;
@JsonProperty("user_age")
private Integer userAge;
@JsonProperty("email")
private String email;
@JsonProperty("is_korean")
private Boolean isKorean; //default is false
But the best practice is to use the default method, without blocking @Data, @AllArgsConstructor or @NoArgsConstructor. And if there is a need to specially match a variable to a different name, we just use @JsonProperty("..").