Enable support for java.time.* with Jackson-jr

Jackson-jr is super neat, but does not (yet) support serialization of java.time.* (e.g. java.time.LocalDate, LocalDateTime) out-of-the-box. However, it is actually super easy to extend it. My example is in Groovy, but it will also work in Java if you add some boilerplate code such as “;”.

First create some utility class to encapsulate the Jackson-jr extension:

package test.json

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.jr.ob.JSON
import com.fasterxml.jackson.jr.ob.JacksonJrExtension
import com.fasterxml.jackson.jr.ob.api.ExtensionContext
import com.fasterxml.jackson.jr.ob.api.ReaderWriterProvider
import com.fasterxml.jackson.jr.ob.api.ValueReader
import com.fasterxml.jackson.jr.ob.api.ValueWriter
import com.fasterxml.jackson.jr.ob.impl.JSONReader
import com.fasterxml.jackson.jr.ob.impl.JSONWriter

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

class JsonUtil {

    static JSON buildJSON() {
        JacksonJrExtension jacksonJrExtension = new JacksonJrExtension() {

            @Override
            protected void register(ExtensionContext ctxt) {
                ctxt.insertProvider(new LocalDateTimeReaderWriterProvider())
            }

        }

        JSON.builder().register(jacksonJrExtension).build()
    }

    static class LocalDateTimeReaderWriterProvider extends ReaderWriterProvider {

        @Override
        ValueReader findValueReader(JSONReader readContext, Class<?> type) {
            type == LocalDateTime ? new LocalDateTimeValueReader() : null
        }

        @Override
        ValueWriter findValueWriter(JSONWriter writeContext, Class<?> type) {
            type == LocalDateTime ? new LocalDateTimeValueWriter() : null
        }

    }

    static class LocalDateTimeValueReader extends ValueReader {

        LocalDateTimeValueReader() {
            super(LocalDateTime)
        }

        @Override
        Object read(JSONReader reader, JsonParser p) throws IOException {
            LocalDateTime.parse(p.text, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        }

    }

    static class LocalDateTimeValueWriter implements ValueWriter {

        @Override
        void writeValue(JSONWriter context, JsonGenerator g, Object value) throws IOException {
            String localDateTimeString = ((LocalDateTime) value).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            context.writeValue(localDateTimeString)
        }

        @Override
        Class<?> valueType() {
            LocalDateTime
        }

    }

}

Define some class that contains the LocalDateTime and should be converted to json and vice versa:

package test.json

import java.time.LocalDateTime

class MyClass {

    LocalDateTime time
    String someOtherValue // just to check that we did not break anything else

}

Finally use it:

package test.json

import com.fasterxml.jackson.jr.ob.JSON

import java.time.LocalDateTime

class Application {

    static final JSON JACKSON = JsonUtil.buildJSON() // static final so we do not create it again and again, it is thread safe.

    static void main(String[] args) {
        final LocalDateTime now = LocalDateTime.now()
        MyClass myObject = new MyClass(time: now, someOtherValue: 'whatever')
        String myObjectJsonString = JACKSON.asString(myObject)
        MyClass myObjectFromJson = JACKSON.beanFrom(MyClass, myObjectJsonString)
        assert myObjectFromJson.time == now
        assert myObjectFromJson.someOtherValue == 'whatever'
    }

}

It hope that it works for you as well

Published by Anders Aaberg

IT-development consultant at Nine A/S MSc. Advanced Computer Science @ University of Leeds, UK

Leave a comment

Your email address will not be published. Required fields are marked *