Jackson Serialize Instant to Nanosecond Issue

天大地大妈咪最大 提交于 2019-12-18 07:12:13

问题


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

It produces JSON like this

{ "timestamp":1421261297.356000000 }

I wonder if there's a way to get rid of the zeros at the end. I want something like:

{ "timestamp":1421261297.356 }

I tried:

mapper.configure( SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false );
mapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true );

But this configuration changes it to millisecond representation 1421261297356. I want the seconds part and the fractional milliseconds part.


回答1:


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<Instant> {

    private ToLongFunction<Instant> getEpochSeconds = Instant::getEpochSecond;
    private ToIntFunction<Instant> 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)) {
                String concatenated = getEpochSeconds.applyAsLong(value) + "." + getNanoseconds.applyAsInt(value);
                generator.writeNumber(new BigDecimal(concatenated));
                return;
            }
        }

        super.serialize(value, generator, provider);
    }
}

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.



来源:https://stackoverflow.com/questions/56345200/jackson-serialize-instant-to-nanosecond-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!