I just released Module 10 in the Master Class of “REST with Spring”:

>> THE "REST WITH SPRING" CLASSES

1. Overview

In this article we’ll explore the various ways we can control if a field is serialized / deserialized by Jackson or not.

2. A Public Field

The simplest way to make sure a field is both serializable and deserializable is to make it public.

Let’s declare a simple class with a public, a package-private and a private

public class MyDtoAccessLevel {
    private String stringValue;
    int intValue;
    protected float floatValue;
    public boolean booleanValue;
    // NO setters or getters
}

Out of the four fields of the class, just the public booleanValue will be serialized to JSON by default:

@Test
public void givenDifferentAccessLevels_whenPublic_thenSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("stringValue")));
    assertThat(dtoAsString, not(containsString("intValue")));
    assertThat(dtoAsString, not(containsString("floatValue")));

    assertThat(dtoAsString, containsString("booleanValue"));
}

3. A Getter Makes a Non-Public Field Serializable and Deserializable

Now, another simple way to make a field – especially a non-public field – serializable, is to add a getter for it:

public class MyDtoWithGetter {
    private String stringValue;
    private int intValue;

    public String getStringValue() {
        return stringValue;
    }
}

We now expect the stringValue field to be serializable, while the other private field not to be, as it has no getter:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoGetter dtoObject = new MyDtoGetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, not(containsString("intValue")));
}

Unintuitively, the getter also makes the private field deserializable as well – because once it has a getter, the field is considered a property.

Let’s look at how that work:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenDeserializable() 
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"stringValue\":\"dtoString\"}";
    ObjectMapper mapper = new ObjectMapper();
    MyDtoWithGetter dtoObject = mapper.readValue(jsonAsString, MyDtoWithGetter.class);

    assertThat(dtoObject.getStringValue(), equalTo("dtoString"));
}

4. A Setter Makes a Non-Public Field Deserializable Only

We saw how the getter made the private field both serializable and deserializable. On the other hand, a setter will only mark the non-public field as deserializable:

public class MyDtoWithSetter {
    private int intValue;

    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }

    public int accessIntValue() {
        return intValue;
    }
}

As you can see, the private intValue field only has a setter this time. We do have a way to access the value, but that’s not a standard getter.

The unmarshalling process for intValue should work correctly:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable() 
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"intValue\":1}";
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);

    assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}

And as we mentioned, the setter should only make the field deserializable, but not serializable:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = new MyDtoSetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("intValue")));
}

5. Make All Fields Globally Serializable

In some cases where, for example, you might not actually be able to modify the source code directly – we need to configure the way Jackson deals with non-public fields from the outside.

That kind of global configuration can be done at the ObjectMapper level, by turning on the AutoDetect function to use either public fields or getter/setter methods for serialization, or maybe turn on serialization for all fields:

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

The following test case verifies all member fields (including non-public) of MyDtoAccessLevel are serializable:

@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, containsString("intValue"));
    assertThat(dtoAsString, containsString("booleanValue"));
}

6. Change the Name of a Property on Serialization/Deserialization

Going beyond controlling which field gets serialized or deserialized, you can also have control over the way a fields maps to JSON and back. I covered this configuration here.

7. Ignore a Field On Serialization or Deserialization

Following this tutorial, we have a guide for how to ignore a field completely on serialization and deserialization.

However, sometimes we only need to ignore the field on either, but not on both. Jackson is flexible enough to accommodate this interesting usecase as well.

The following example shows a User object which contains sensitive password information which shouldn’t be serialized to JSON.

To get there, we simply add the @JsonIgnore annotation on the getter of the password, and enable deserialization for the field by applying the @JsonProperty annotation on the setter:

@JsonIgnore
public String getPassword() {
    return password;
}
@JsonProperty
public void setPassword(String password) {
    this.password = password;
}

Now the password information won’t be serialized to JSON:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    User userObject = new User();
    userObject.setPassword("thePassword");

    String userAsString = mapper.writeValueAsString(userObject);
    assertThat(userAsString, not(containsString("password")));
    assertThat(userAsString, not(containsString("thePassword")));
}

However, the JSON containing the password will be successfully deserialized to the User object:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect() 
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = "{\"password\":\"thePassword\"}";
    ObjectMapper mapper = new ObjectMapper();

    User userObject = mapper.readValue(jsonAsString, User.class);

    assertThat(userObject.getPassword(), equalTo("thePassword"));
}

8. Conclusion

This tutorial goes over the basic of how Jackson chooses which field is serialized/deserialized and which gets ignored in the process and of course how to get fully control over it.

You may also get to the next step in understanding Jackson 2 by diving deeper with articles such as ignoring a fielddeserializing a JSON Array to a Java Array or Collection.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

Go deeper into building a REST API with Spring:

>> CHECK OUT THE COURSE

  • Excellent compilation of most useful Jackson usecases. Thanks for sharing (y)

  • Kailash Joshi

    Class com.fasterxml.jackson.databind.deser.impl.FieldProperty can not access a member “protected”
    and surprisingly serialization is working fine

    • That’s interesting – can you open a PR (with a quick test showing that behavior) on the Github repo? I’ll be happy to have a look.
      Cheers,
      Eugen.