1. Overview

In this tutorial, we'll investigate how we can serialize properties with different access modifiers. Additionally, we'll look at the usage of accessor methods during serialization.

2. Serializing Package-private fields

Let's start with package-private fields.

By default, Jackson can't serialize package-private fields. This is because Jackson needs public accessor methods for getting field values:

public class DefaultPerson {

    String name;
    int age;

    public DefaultPerson() {
    }

    public DefaultPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Here, DefaultPerson class has two package-private fields with no public accessors.

Similar to private field serialization, serialization of package-private fields results in an exception:

@Test(expected = JsonProcessingException.class)
public void shouldNotSerialize_WithDefaultFields() throws JsonProcessingException {
    DefaultPerson defaultPerson = new DefaultPerson("john", 21);

    objectMapper.writeValueAsString(defaultPerson);
}

This is the case when we use Jackson with its default settings. However, we can change the visibility settings of Jackson to serialize package-private fields.

3. Serialize Public Fields using Jackson

Now, let's look at public field serialization.

Unlike package-private fields, Jackson can serialize public fields even if they don't have public accessors.

We have the PublicPerson class:

public class PublicPerson {

    public String name;
    public int age;

    public PublicPerson() {
    }

    public PublicPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

When we serialize an instance of PublicPerson, it successfully runs:

@Test
public void shouldSerialize_WithPublicFields() throws JsonProcessingException {
    PublicPerson publicPerson = new PublicPerson("john", 21);

    String json = objectMapper.writeValueAsString(publicPerson);

    assertThat(json).isEqualTo("{\"name\":\"john\",\"age\":21}");
}

4. Serialize with Getter Methods

Jackson can also serialize with getter methods even if the fields are private:

public class GetterPerson {

    private String name;
    private int age;

    public GetterPerson() {
    }

    public GetterPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

Here we have GetterPerson with two fields and corresponding getter methods.

When Jackson serializes an instance, it extracts the JSON field names from the JavaBean style methods:

@Test
public void shouldSerialize_WithGetters() throws JsonProcessingException {
    GetterPerson getterPerson = new GetterPerson("john", 21);

    String json = objectMapper.writeValueAsString(getterPerson);

    assertThat(json).isEqualTo("{\"name\":\"john\",\"age\":21}");
}

Here, name is extracted from getName() and age is extracted from getAge() method.

5. Serialize Custom JavaBean Style Methods

In a class, there can be methods that don't have a field directly backing it. However, if this method conforms to JavaBean rules, Jackson uses it in serialization:

public class CustomGetterPerson {

    private String name;
    private int age;

    public CustomGetterPerson() {
    }

    public CustomGetterPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return 999;
    }

    public String getName() {
        return name;
    }

    public int getPreviousAge() {
        return age - 1;
    }

    public int nextAge() {
        return age + 1;
    }
}

Here, the getPreviousAge() method doesn't have a backing field, previousAge.

When Jackson serializes the instance, resulting JSON also includes a field named as previousAge. Note that serialized class doesn't have a field with that name, but has a method. Also note that, since nextAge() method isn't a valid JavaBean method, it isn't included in resulting JSON:

@Test
public void shouldSerialize_WithCustomGetter_AndAdditionalMethods() throws JsonProcessingException {
    CustomGetterPerson customGetterPerson = new CustomGetterPerson("john", 21);

    String json = objectMapper.writeValueAsString(customGetterPerson);

    assertThat(json).isEqualTo("{\"name\":\"john\",\"age\":999,\"previousAge\":20}");
}

6. Summary

In this tutorial, we've investigated serializing properties with different access modifiers. Additionally, we've investigated the usage of methods during serialization.

As always, the source code is available on Github.