web-dev-qa-db-fra.com

Jackson, Java.time, ISO 8601, sérialiser sans millisecondes

J'utilise Jackson 2.8 et dois communiquer avec une API qui n'autorise pas les millisecondes dans les horodatages ISO 8601.

Le format attendu est le suivant: "2016-12-24T00:00:00Z"

J'utilise le JavaTimeModule de Jackson avec WRITE_DATES_AS_TIMESTAMPS défini sur false.

Mais cela imprimera des millisecondes.

J'ai donc essayé d'utiliser objectMapper.setDateFormat qui n'a rien changé.

Ma solution actuelle est la suivante:

ObjectMapper om = new ObjectMapper();

DateTimeFormatter dtf = new DateTimeFormatterBuilder()
    .appendInstant(0)
    .toFormatter();

JavaTimeModule jtm = new JavaTimeModule();
jtm.addSerializer(Instant.class, new JsonSerializer<Instant>() {
    @Override
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        gen.writeString(dtf.format(value));
    }
});

om.registerModule(jtm);

Je remplace le sérialiseur par défaut pour Instant.class qui fonctionne.


Existe-t-il un moyen intéressant d'utiliser un paramètre de configuration pour résoudre ce problème?

12
Benjamin M

Mettre à jour:

Ajoutez simplement une annotation @JsonFormat avec le format de date au-dessus de la propriété Instant. C'est très facile.

Dans le cas où vous avez un ObjectMapper avec le JavaTimeModule comme suit:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

Si vous avez une classe avec une propriété Instant, vous devez ajouter l'annotation @JsonFormat et mettre le modèle de date qui n'a pas millisecondes. Ce serait comme après:

public static class TestDate {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "UTC")
    Instant instant;

    //getters & setters
}

Donc si vous sérialisez un objet sur Json cela fonctionne parfaitement:

String json = mapper.writeValueAsString(testDate);
System.out.println(json); 

Sortie

{"instant": "2016-11-10 06:03:06"}


Ancienne réponse. Je ne sais pas pourquoi mais ça ne marche pas correctement:

Vous pouvez utiliser le Jackson2ObjectMapperBuilder pour le construire.

Vous devez juste ajouter le dateFormat que vous voulez. Ce serait quelque chose comme:

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

ObjectMapper mapper = Jackson2ObjectMapperBuilder
            .json()       
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 
            .modules(new JavaTimeModule())
            .dateFormat(dateFormat)
            .build();
4
Pau

Voici une alternative que vous pouvez définir globalement, mais vous devrez utiliser ZonedDateTime avec un formateur instantané, car nous ne pouvons pas définir le format du Instant Serializer fourni avec Java Time Module.

L'utilisation de la date et l'heure zonées ne se produira pas instantanément car jackson sérialise l'identifiant de zone séparément et est désactivé par défaut. Donc, techniquement, cela revient à appliquer le formateur à Instant

Lorsqu'il est utilisé de cette manière, le sérialiseur ZonedDateTime délègue la sérialisation à InstantBaseSerializer et utilise le format personnalisé spécifié.

@RunWith(JUnit4.class)
public class InstantNoMillisTest {

    private ObjectMapper objectMapper;

    @Before
    public void init() {
        JavaTimeModule module = new JavaTimeModule();
        ZonedDateTimeSerializer zonedDateTimeSerializer = new ZonedDateTimeSerializer(new DateTimeFormatterBuilder().appendInstant(0).toFormatter());
        module.addSerializer(ZonedDateTime.class, zonedDateTimeSerializer);
        module.addDeserializer(ZonedDateTime.class, InstantDeserializer.ZONED_DATE_TIME);

        objectMapper = Jackson2ObjectMapperBuilder.json()
                .modules(module)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .build();
    }

    @Test
    public void serialize() throws IOException {
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        String noMillis = objectMapper.writeValueAsString(zonedDateTime);
        System.out.print(noMillis);
    }

    @Test
    public void deserialize() throws IOException {
        String dateTime = "\"2017-10-26T12:54:59Z\"";
        ZonedDateTime noMillis = objectMapper.readValue(dateTime, ZonedDateTime.class);
        System.out.print(noMillis);
    }
}
4
Veeram

Voici un code Kotlin de mise en forme des champs Instant, afin qu'ils ne contiennent pas des millisecondes, vous pouvez utiliser des formateurs de date personnalisés

ObjectMapper().apply {
        val javaTimeModule = JavaTimeModule()
        javaTimeModule.addSerializer(Instant::class.Java, Iso8601WithoutMillisInstantSerializer())
        registerModule(javaTimeModule)
        disable(WRITE_DATES_AS_TIMESTAMPS)
    }

private class Iso8601WithoutMillisInstantSerializer
        : InstantSerializer(InstantSerializer.INSTANCE, false, DateTimeFormatterBuilder().appendInstant(0).toFormatter())
0
ZZ 5