杰克逊序列化即时到纳秒问题

Jackson Serialize Instant to Nanosecond Issue

Jackson 序列化 java.time.Instant,默认启用 WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS

它产生 JSON 这样的

{ "timestamp":1421261297.356000000 }

我想知道是否有办法去掉末尾的零。我想要这样的东西:

{ "timestamp":1421261297.356 }

我试过了:

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

但此配置将其更改为毫秒表示1421261297356。我想要秒部分和小数毫秒部分。

当我们使用 Java 8 Time 包和 Jackson 好主意是使用 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 方法。看起来总是在纳秒值的末尾添加零。

我们可以编写 InstantSerializer 以所需方式对其进行序列化。因为这个项目中的 classes 还没有准备好轻松扩展,所以我们需要实现许多不需要的方法和构造函数。我们还需要在我们的项目 com.fasterxml.jackson.datatype.jsr310.ser 包中创建并在那里创建我们的实现。请参阅以下示例:

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)) {
                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("%1s", String.valueOf(value)).replace(' ', '0');
    }
}

以及如何使用它的示例:

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;
    }
}

以上代码打印:

{"timestamp":1559074287.223}

如果您想在所有情况下都删除所有零,请编写您自己的 getNanosecondsShortInstantSerializer class.

中声明的函数

我采纳了上面 Michal 的想法并将现有的 com.fasterxml.jackson.datatype.jsr310.DecimalUtils#toBigDecimal(long seconds, int nanoseconds) 包装在一个序列化程序中

class ShortInstantSerializer extends StdSerializer<Instant> {
    ShortInstantSerializer() {
        super(Instant.class);
    }

    @Override
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeNumber(DecimalUtils.toBigDecimal(value.getEpochSecond(), value.getNano()));
    }
}

此外,如果您要在某个时候读回它,该值将被反序列化为 Double。要返回执行此操作(感谢 inverwebs!)

public static Instant toInstant(Double d) {
    long seconds = d.longValue();
    long micros = Math.round((d - seconds) * 1_000_000);
    return Instant.ofEpochSecond(seconds).plus(micros , ChronoUnit.MICROS);
}