Jackson Serialize Instant to Nanosecond Issue

后端 未结 2 1692
予麋鹿
予麋鹿 2020-11-28 16:01

Jackson serialises java.time.Instant with WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS enabled by default.

It produces JSON

2条回答
  •  伪装坚强ぢ
    2020-11-28 16:46

    When we are working with Java 8 Time package and Jackson good idea is to use jackson-modules-java8 project which serves many serialisers and deserialisers ready to use. To enable it we need to register JavaTimeModule module. To serialise Instant InstantSerializer is used. When we check how it is implemented we will find out that behind scene DecimalUtils.toDecimal method is used. It looks like there is always zeros added at the end of nanoseconds value.

    We can write our InstantSerializer which serialises it in desired way. Because classes in this project are not ready to easily extend we need to implement many unwanted methods and constructors. Also we need create in our project com.fasterxml.jackson.datatype.jsr310.ser package and create our implementation there. See below example:

    package com.fasterxml.jackson.datatype.jsr310.ser;
    
    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.SerializerProvider;
    
    import java.io.IOException;
    import java.math.BigDecimal;
    import java.time.Instant;
    import java.time.format.DateTimeFormatter;
    import java.util.function.ToIntFunction;
    import java.util.function.ToLongFunction;
    
    public class ShortInstantSerializer extends InstantSerializerBase {
    
        private ToLongFunction getEpochSeconds = Instant::getEpochSecond;
        private ToIntFunction getNanoseconds = i -> i.getNano() / 1_000_000;
    
        public ShortInstantSerializer() {
            super(Instant.class, Instant::toEpochMilli, Instant::getEpochSecond, Instant::getNano, null);
        }
    
        protected ShortInstantSerializer(ShortInstantSerializer base, Boolean useTimestamp, Boolean useNanoseconds, DateTimeFormatter formatter) {
            super(base, useTimestamp, useNanoseconds, formatter);
        }
    
        @Override
        protected JSR310FormattedSerializerBase withFormat(Boolean useTimestamp, DateTimeFormatter formatter, JsonFormat.Shape shape) {
            return new ShortInstantSerializer(this, useTimestamp, null, formatter);
        }
    
        @Override
        public void serialize(Instant value, JsonGenerator generator, SerializerProvider provider) throws IOException {
            if (useTimestamp(provider)) {
                if (useNanoseconds(provider)) {
                    generator.writeNumber(new BigDecimal(toShortVersion(value)));
                    return;
                }
            }
    
            super.serialize(value, generator, provider);
        }
    
        private String toShortVersion(final Instant value) {
            return getEpochSeconds.applyAsLong(value) + "." + padWithZeros(getNanoseconds.applyAsInt(value));
        }
    
        private String padWithZeros(final int value) {
            return String.format("%1$3s", String.valueOf(value)).replace(' ', '0');
        }
    }
    

    And example how to use it:

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.datatype.jsr310.ser.ShortInstantSerializer;
    
    import java.time.Instant;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            javaTimeModule.addSerializer(Instant.class, new ShortInstantSerializer());
    
            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule(javaTimeModule);
            mapper.disable(SerializationFeature.INDENT_OUTPUT);
    
            System.out.println(mapper.writeValueAsString(new Element()));
        }
    }
    
    class Element {
    
        private Instant timestamp = Instant.now();
    
        public Instant getTimestamp() {
            return timestamp;
        }
    
        public void setTimestamp(Instant timestamp) {
            this.timestamp = timestamp;
        }
    }
    

    Above code prints:

    {"timestamp":1559074287.223}
    

    If you want to rid of all zeros in all cases write your own getNanoseconds function declared in ShortInstantSerializer class.

提交回复
热议问题