I'm using Jackson v2 in a JAX-RS app, and I'm trying to figure out if it is possible to configure "ObjectMapper" to drop fields with transient modifier, but only during serialisation.
Here's a DTO to illustrate my use case (getters & setters omitted)
public class User {
private String email;
private transient String password;
}
With the above DTO in mind, I would like to have an object mapper that drops the password field when serialising, and includes it during deserialisation.
The only thing I could find regarding transient is:
MapperFeature.PROPAGATE_TRANSIENT_MARKER
But that config isn't relevant to my requirements.
P.S - No annotation are to be used, as the DTO's are not to be coupled to any library, plus I don't necessarily have control over them.
Don't know, looks like transient
attribute modifier plus PROPAGATE_TRANSIENT_MARKER
just does it. Deserialization is straight forward. There's some fuss with the transient marker during serialization, though. You need to provide getters for the fields, otherwise you'll be stuck with an exception "Unrecognized field ... not marked as ignorable".
Reacting to additional requirements I added a version using jackson mixins. Note that the mixin version produces json with an empty password field, while the PROPAGATE_TRANSIENT_MARKER version produces no field at all.
PROPAGATE_TRANSIENT_MARKER version
public class SerializeDeserializeAttributes {
private final ObjectMapper mapper = new ObjectMapper();
public <T> T fromJson(String json, Class<T> c) throws IOException {
synchronized (mapper) {
mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false);
return mapper.readValue(json, c);
}
}
public String toJson(Object o) throws JsonProcessingException {
synchronized (mapper) {
mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
return mapper.writeValueAsString(o);
}
}
private static final String jsonFull = "{\"name\":\"A\",\"email\":\"a@a\",\"password\":\"a\",\"width\":1,\"height\":1}";
private static final String jsonPartial = "{\"name\":\"A\",\"email\":\"a@a\",\"width\":1,\"height\":1}";
private static final User user = new User("A", "a@a", "a", 1, 1);
@Test
public void serializeDeserialize() throws IOException {
assertEquals(user, fromJson(jsonFull, User.class));
assertEquals(jsonPartial, toJson(user));
assertEquals(user, fromJson(jsonFull, User.class));
assertEquals(jsonPartial, toJson(user));
}
}
Mixin version
public class SerializeDeserializeAttributesMixin {
public abstract class UserMixin {
@JsonSerialize(using = PwdSerializer.class)
transient String password;
}
static class PwdSerializer extends StdSerializer<String> {
public PwdSerializer() {
this(String.class);
}
private PwdSerializer(Class<String> t) {
super(t);
}
@Override
public void serialize(String s, JsonGenerator jg, SerializerProvider sp) throws IOException {
jg.writeString("");
}
}
private static final String jsonFull = "{\"name\":\"A\",\"email\":\"a@a\",\"password\":\"a\",\"width\":1,\"height\":1}";
private static final String jsonPartialMixin = "{\"name\":\"A\",\"email\":\"a@a\",\"password\":\"\",\"width\":1,\"height\":1}";
private static final User user = new User("A", "a@a", "a", 1, 1);
private static final ObjectMapper mapperMixin = new ObjectMapper();
static {
mapperMixin.addMixIn(User.class, UserMixin.class);
}
@Test
public void serializeDeserializeUsingMixin() throws IOException {
assertEquals(user, mapperMixin.readValue(jsonFull, User.class));
assertEquals(jsonPartialMixin, mapperMixin.writeValueAsString(user));
}
}
That's the User class.
class User {
private String name;
private String email;
private transient String password;
private int width;
private int height;
public User() {
}
User(String name, String email, String password, int width, int height) {
this.name = name;
this.email = email;
this.password = password;
this.width = width;
this.height = height;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Float.compare(user.width, width) == 0 &&
Float.compare(user.height, height) == 0 &&
Objects.equals(name, user.name) &&
Objects.equals(email, user.email) &&
Objects.equals(password, user.password);
}
@Override
public int hashCode() {
return Objects.hash(name, email, password, width, height);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
", width=" + width +
", height=" + height +
'}';
}
}