5 votes

Jackson sérialise la question de l'instant à la nanoseconde

Jackson sérialise java.time.Instant avec WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS activé par défaut.

Il produit JSON comme ceci

{ "timestamp":1421261297.356000000 }

Je me demande s'il y a un moyen de se débarrasser des zéros à la fin. Je veux quelque chose comme :

{ "timestamp":1421261297.356 }

J'ai essayé :

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

Mais cette configuration la change en représentation en millisecondes. 1421261297356 . Je veux la partie secondes et la partie millisecondes fractionnées.

7voto

Michał Ziober Points 5787

Lorsque nous travaillons avec Java 8 Time et Jackson Une bonne idée est d'utiliser jackson-modules-java8 qui propose de nombreux sérialiseurs et désérialiseurs prêts à l'emploi. Pour l'activer, nous devons enregistrer JavaTimeModule module. Pour sérialiser Instant InstantSerializer est utilisé. Lorsque nous vérifions comment il est mis en œuvre, nous découvrons que derrière la scène DecimalUtils.toDecimal est utilisée. Il semble qu'il y ait toujours des zéros ajoutés à la fin de la valeur des nanosecondes.

Nous pouvons écrire notre InstantSerializer qui le sérialise de la manière souhaitée. Parce que les classes de ce projet ne sont pas prêtes à être facilement étendues, nous devons implémenter de nombreuses méthodes et constructeurs indésirables. Nous devons également créer dans notre projet com.fasterxml.jackson.datatype.jsr310.ser et y créer notre implémentation. Voir l'exemple ci-dessous :

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

Et des exemples pour l'utiliser :

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

Le code ci-dessus s'imprime :

{"timestamp":1559074287.223}

Si vous voulez vous débarrasser de tous les zéros dans tous les cas, écrivez votre propre programme. getNanoseconds déclarée dans ShortInstantSerializer classe.

1voto

Pawel Zieminski Points 21

J'ai pris l'idée de Michal ci-dessus et j'ai enveloppé le système existant de com.fasterxml.jackson.datatype.jsr310.DecimalUtils#toBigDecimal(long seconds, int nanoseconds) dans un sérialiseur

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

De plus, si vous devez le relire à un moment donné, la valeur sera désérialisée en un Double. Pour revenir en arrière, faites ceci (merci 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);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X