Why Jackson not produce "propertie id"?
Result run Main
{id=1, name=Marcelo, [email protected], password=123456, token=t1234, refre ...}
{id=1, name=Marcelo, [email protected], password=null, enable=true}
propertie id > {"name":"Marcelo","email":"[email protected]","enable":true}
The problem consist in propertie id mapped with
@JsonProperty(access = Access.WRITE_ONLY) When Serializable not produces propertie and your value
jackson 2.13.0, but bug existin in others versions
In github has project simulator the problem Bug Jackson Serializable propertie
Show code here
Main.class
package com;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class Main {
private ObjectMapper model;
public Main() {
model = modelMapper();
}
public static void main(String[] args) {
var main = new Main();
var userEntity = new User();
System.out.println(userEntity.toString());
var user = main.getModel().convertValue(userEntity, UserDTO.class);
System.out.println(user.toString());
try {
System.out.println(main.getModel().writeValueAsString(user));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
public ObjectMapper getModel() {
return model;
}
private ObjectMapper modelMapper() {
var model = new ObjectMapper();
model.registerModule(new SimpleModule());
model.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
model.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
model.configure(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID, false);
return model;
}
}
UserDTO.class
package com;
import java.io.Serializable;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty(access = Access.WRITE_ONLY)
private Long id;
private String name;
private String email;
@JsonProperty(access = Access.READ_ONLY)
private String password;
private Boolean enable = false;
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UserDTO other = (UserDTO) obj;
return Objects.equals(id, other.id);
}
@Override
public String toString() {
return "{id=" + id + ", name=" + name + ", email=" + email + ", password=" + password + ", enable="
+ enable + "}";
}
}
User.class
package com;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id = 1L;
private String name = "Marcelo";
private String email = "[email protected]";
private String password = "123456";
private String token = "t1234";
private String refresh = "t2345";
private Boolean enable = true;
private Boolean hashAble = true;
private Date dataLastChange = new Date();
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getRefresh() {
return refresh;
}
public void setRefresh(String refresh) {
this.refresh = refresh;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public Boolean getHashAble() {
return hashAble;
}
public void setHashAble(Boolean hashAble) {
this.hashAble = hashAble;
}
public Date getDataLastChange() {
return dataLastChange;
}
public void setDataLastChange(Date dataLastChange) {
this.dataLastChange = dataLastChange;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
return Objects.equals(id, other.id);
}
@Override
public String toString() {
return "{id=" + id + ", name=" + name + ", email=" + email + ", password=" + password + ", token=" + token
+ ", refresh=" + refresh + ", enable=" + enable + ", hashAble=" + hashAble + ", dataLastChange="
+ dataLastChange + "}";
}
}
public static final JsonProperty.Access WRITE_ONLY. Access setting that means that the property may only be written (set) for deserialization, but will not be read (get) on serialization, that is, the value of the property is not included in serialization.
public static final JsonProperty.Access READ_WRITE. Access setting that means that the property will be accessed for both serialization (writing out values as external representation) and deserialization (reading values from external representation), regardless of visibility rules.
@JsonProperty is used to mark non-standard getter/setter method to be used with respect to json property.
The @JsonProperty annotation is used to map property names with JSON keys during serialization and deserialization. By default, if you try to serialize a POJO, the generated JSON will have keys mapped to the fields of the POJO.
The reference documentation is pretty clear about this. In WRITE_ONLY
you can read the following:
Access setting that means that the property may only be written (set) for deserialization, but will not be read (get) on serialization, that is, the value of the property is not included in serialization.
This clearly states that properties annotated with @JsonProperty(access = Access.WRITE_ONLY)
will not be included while serializing (Java object to JSON in your case) the object that contains them.
It seems to me that you swapped the access type. I guess that the following is what you want:
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty(access = Access.READ_ONLY)
private Long id;
private String name;
private String email;
@JsonProperty(access = Access.WRITE_ONLY)
private String password;
private Boolean enable = false;
(...)
}
Given that you are using ObjectMapper
to map from User
to UserDTO
, then the only option is to get rid of @JsonProperty
in id
as follows:
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private String email;
@JsonProperty(access = Access.WRITE_ONLY)
private String password;
private Boolean enable = false;
(...)
}
I suggest you not to use the Jackson library for entity mapping as it does it by serializing and deserializing an object. WHich is slow and can lead to a lot of errors that are hard to detect.
I recommend that you use a mapping library like mapstruct.
For example, the following will create a mapper that mapps from UserDto to User and vice versa. It will ignore the password when mapping to UserDto. All fields that are named the same will automatically be found and mapped.
@Mapper
public abstract class UserMapper {
public static final UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(target = "password", ignore = true)
public abstract UserDto toUserDto(User user);
public abstract User toUser(UserDto userDto);
}
usage would be like this in your code:
public class Main {
public static void main(String[] args) {
var user= new User();
System.out.println(userEntity.toString());
var userDto = UserMapper.INSTANCE.toUserDto(userEntity);
System.out.println(userDto.toString());
try {
System.out.println((new ObjectMapper()).writeValueAsString(userDto));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
The jackson library is great for serializing OR deserializing, but this process is not as fast and reliable as direct mapping one entity into another one. I think the following excerpt from the Jackson documentation already hints at that it is not the recommended way to use the convert function for entity to entity conversion:
Further note that it is possible that in some cases behavior does differ from full serialize-then-deserialize cycle: in most case differences are unintentional (that is, flaws to fix) and should be reported, but the behavior is not guaranteed to be 100% the same: the goal is to allow efficient value conversions for structurally compatible Objects, according to standard Jackson configuration.
Finally, this functionality is not designed to support "advanced" use cases, such as conversion of polymorphic values, or cases where Object Identity is used.
source: ObjectMapper#convertValue
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With