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.
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)) {
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.
I took Michal's idea above and wrapped the existing com.fasterxml.jackson.datatype.jsr310.DecimalUtils#toBigDecimal(long seconds, int nanoseconds)
in a serializer
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()));
}
}
Also, if you're going to be reading it back at some point, the value will be deserialized into a Double. To go back do this (thanks 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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With