1. Overview

In this tutorial, we'll investigate how to format date fields for serialization using Jackson. Firstly, we'll look at Jackson's default behavior. Then we'll investigate other options Jackson provides.

2. Default Date Formatting using Jackson

In Jackson, default date serializer is com.fasterxml.jackson.databind.ser.std.DateSerializer. This serializer formats date properties as timestamp by default:

@Before
public void setUp() {
    Instant instant = LocalDateTime.of(2008, 2, 3, 12, 45)
                                   .toInstant(ZoneOffset.UTC);
    birthDate = Date.from(instant);
}

@Test
public void shouldSerializeDate_AsTimestamp_ByDefault() throws JsonProcessingException {
    class Person {

        private Date birthDate;

        public Date getBirthDate() {
            return birthDate;
        }

        public void setBirthDate(Date birthDate) {
            this.birthDate = birthDate;
        }
    }

    final Person person = new Person();
    person.setBirthDate(birthDate);

    String json = objectMapper.writeValueAsString(person);

    assertThat(json).isEqualTo("{\"birthDate\":1202042700000}");
}

Here, we have Person class with birthDate field. Note that there is no specific Jackson annotation to configure the serialization of this field. As a result, birthDate is serialized as 1202042700000.

3. Format Date with @JsonFormat using pattern Attribute

Next, we'll see how we can use @JsonFormat to format date fields.

Firstly @JsonFormat annotation isn't specific to only date fields in that we can control serialization details of any property. But to format date properties, we should assign a date format pattern to pattern attribute:

@Test
public void shouldFormatDate_WhenJsonFormatIsGiven_WithPattern() throws JsonProcessingException {
    class Person {

        @JsonFormat(pattern = "yyyy-MM-dd")
        private Date birthDate;

        public Date getBirthDate() {
            return birthDate;
        }

        public void setBirthDate(Date birthDate) {
            this.birthDate = birthDate;
        }
    }

    final Person person = new Person();
    person.setBirthDate(birthDate);

    String json = objectMapper.writeValueAsString(person);

    assertThat(json).isEqualTo("{\"birthDate\":\"2008-02-03\"}");
}

Here, Jackson serializes birthDate field as "2008-02-03". This is because we've set the pattern attribute as "yyyy-MM-dd".

4. Format Date with @JsonFormat using shape Attribute

@JsonFormat also contains shape attribute whose exact behavior changes according to the property type. To format Date properties, we can set shape as Shape.String:

@Test
public void shouldFormatDate_WhenJsonFormatIsGiven_WithShape() throws JsonProcessingException {
    class Person {

        @JsonFormat(shape = Shape.STRING)
        private Date birthDate;

        public Date getBirthDate() {
            return birthDate;
        }

        public void setBirthDate(Date birthDate) {
            this.birthDate = birthDate;
        }
    }

    final Person person = new Person();
    person.setBirthDate(birthDate);

    String json = objectMapper.writeValueAsString(person);

    assertThat(json).isEqualTo("{\"birthDate\":\"2008-02-03T12:45:00.000+0000\"}");
}

Here, we aren't supplying any date format pattern and only setting the shape attribute. Thus, DateSerializer uses a predefined date format pattern. As a result, birthDate is serialized as "2008-02-03T12:45:00.000+0000".

5. Format Date using Custom Date Serializer

Now let's look at how we can use a custom serializer to format dates.

While previous options may suffice for most of the use cases, we may have more complex requirements. In this case, we can write a custom date serializer.

Firstly, we should create a new class extending StdSerializer, since it is the recommended base class to use:

public class CustomDateSerializer extends StdSerializer<Date> {

    protected CustomDateSerializer() {
        this(null);
    }

    protected CustomDateSerializer(Class<Date> t) {
        super(t);
    }

    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ssZZZ");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

        jsonGenerator.writeString(dateFormat.format(date));
    }
}

Here, we have a basic implementation and it formats the dates with a predefined pattern.

Secondly, for this serializer to take effect, we should put a @JsonSerialize annotation on the related Date field. We should also set the using attribute with the class name of our custom serializer:

@Test
public void shouldSerialize() throws JsonProcessingException {
    Person person = new Person();
    person.setBirthDate(birthDate);

    String json = objectMapper.writeValueAsString(person);

    assertThat(json).isEqualTo("{\"birthDate\":\"2008/02/03 12:45:00+0000\"}");
}

static class Person {

    @JsonSerialize(using = CustomDateSerializer.class)
    private Date birthDate;

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
}

After this configuration, Jackson serializes birthDate field as "2008-02-03 02:45:00".

6. Summary

In this tutorial, we've looked at different options for formatting date fields during serialization.

As always, the source code for all examples is available on Github.