2017-09-17 6 views
3

date de décalage sur mesure un modèle qui a une propriété d'horodatage:Jackson: parse J'ai le temps

class Model { 
    @JsonProperty("timestamp") 
    private OffsetDateTime timestamp; 
} 

Le format de l'horodatage est comme suivant:

2017-09-17 13:45:42.710576+02 

OffsetDateTime ne peut pas analyser ceci:

com.fasterxml.jackson.databind.exc.InvalidFormatException: ne peut pas désérialiser valeur de type java.time.OffsetDateTime de chaîne "17/09/2017 13: 45: 42,710576 + 02": Texte '17/09/2017 13: 45: 42,710576 + 02' n'a pas pu être analysée à l'index 10

Comment puis-je résoudre ce problème ?

Répondre

2

Vous devez dire Jackson dans quel format la date est. Fondamentalement, vous avez year-month-day suivi par hour:minute:second.microseconds et le décalage avec 2 chiffres (+02). Ainsi, votre modèle sera:

@JsonProperty("timestamp") 
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSSx") 
private OffsetDateTime timestamp; 

Jetez un oeil à all the date/time patterns pour une explication plus détaillée.


Si vous souhaitez conserver le même décalage (+02) dans le OffsetDateTime, ne pas oublier de régler la DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE option-false.

Si cette option est réglée sur true (dans mes tests), le résultat est converti en UTC (mais il convertit en fait à tout ce fuseau horaire est configuré à Jackson):

2017-09-17T11: 45 : 42.710576Z

Si je mets à false, le décalage utilisé dans l'entrée est conservée:

2017-09-17T13: 45: 42,710576 + 02: 00


Le code ci-dessus fonctionne exactement 6 chiffres après la virgule. Mais si cette quantité varie, vous pouvez utiliser des motifs facultatifs, délimités par [].

Exemple: si l'entrée peut avoir 6 ou 3 chiffres décimaux, je peux utiliser pattern = "yyyy-MM-dd HH:mm:ss.[SSSSSS][SSS]x". Les sections facultatives [SSSSSS] et [SSS] indiquent à l'analyseur de considérer 6 ou 3 chiffres. Le problème avec les motifs optionnels est que, lors de la sérialisation, il imprime tous les motifs (il imprime donc deux fois la fraction de seconde: avec 6 et avec 3 chiffres).


Une autre alternative est de créer serializers personnalisés et désérialiseurs (en étendant com.fasterxml.jackson.databind.JsonSerializer et com.fasterxml.jackson.databind.JsonDeserializer):

public class CustomDeserializer extends JsonDeserializer<OffsetDateTime> { 

    private DateTimeFormatter formatter; 

    public CustomDeserializer(DateTimeFormatter formatter) { 
     this.formatter = formatter; 
    } 

    @Override 
    public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { 
     return OffsetDateTime.parse(parser.getText(), this.formatter); 
    } 
} 

public class CustomSerializer extends JsonSerializer<OffsetDateTime> { 

    private DateTimeFormatter formatter; 

    public CustomSerializer(DateTimeFormatter formatter) { 
     this.formatter = formatter; 
    } 

    @Override 
    public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException { 
     gen.writeString(value.format(this.formatter)); 
    } 
} 

Ensuite, vous pouvez vous inscrire ceux du JavaTimeModule. Comment configurer cela dépendra de l'environnement que vous utilisez (exemple: au printemps, vous pouvez configurer dans le xml files). Je vais le faire par programme comme un exemple.

D'abord, je crée le formatter, en utilisant un java.time.format.DateTimeFormatterBuilder:

DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
    // date/time 
    .appendPattern("yyyy-MM-dd HH:mm:ss") 
    // optional fraction of seconds (from 0 to 9 digits) 
    .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).optionalEnd() 
    // offset 
    .appendPattern("x") 
    // create formatter 
    .toFormatter(); 

Cette formatter accepte une fraction de seconde en option avec 0 à 9 chiffres. Puis-je utiliser les classes personnalisées ci-dessus et de les inscrire dans le ObjectMapper:

// set formatter in the module and register in object mapper 
ObjectMapper mapper = new ObjectMapper(); 
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false); 
JavaTimeModule module = new JavaTimeModule(); 
module.addSerializer(OffsetDateTime.class, new CustomSerializer(formatter)); 
module.addDeserializer(OffsetDateTime.class, new CustomDeserializer(formatter)); 
mapper.registerModule(module); 

éleminez aussi l'annotation @JsonFormat du champ:

@JsonProperty("timestamp") 
private OffsetDateTime timestamp; 

et accepte maintenant des valeurs comme 2017-09-17 13:45:42+02 (aucune fraction de seconde) et 2017-09-17 13:45:42.71014+02 (5 chiffres décimaux). Il peut analyser de 0 à 9 chiffres décimaux (9 est le maximum pris en charge par l'API), et il imprime exactement la même quantité lors de la sérialisation.


L'alternative ci-dessus est très flexible car elle permet de définir le formateur dans les classes personnalisées. Mais il définit également la sérialisation et la désérialisation pour tous les champs OffsetDateTime.

Si vous ne voulez pas, vous pouvez également créer une classe avec un formatter fixe:

static class CustomDeserializer extends JsonDeserializer<OffsetDateTime> { 

    private DateTimeFormatter formatter = // create formatter as above 

    // deserialize method is the same 
} 

static class CustomSerializer extends JsonSerializer<OffsetDateTime> { 

    private DateTimeFormatter formatter = // create formatter as above 

    // serialize method is the same 
} 

Ensuite, vous pouvez les ajouter à seulement les champs que vous souhaitez, en utilisant les annotations com.fasterxml.jackson.databind.annotation.JsonSerialize et com.fasterxml.jackson.databind.annotation.JsonDeserialize:

@JsonProperty("timestamp") 
@JsonSerialize(using = CustomSerializer.class) 
@JsonDeserialize(using = CustomDeserializer.class) 
private OffsetDateTime timestamp; 

Avec cela, vous n'avez pas besoin d'enregistrer les serializers personnalisés dans le module, et seul le champ annoté utiliser les classes personnalisées (les autres champs OffsetDateTime utiliseront les paramètres par défaut).

+0

Intéressant, je ne savais pas que vous pouviez définir le format de la date comme ça. Dois-je utiliser un 'OffsetDateTime' de' ZonedDateTime' pour cela? –

+0

@KwersT Si vous n'avez que le décalage +02, je suggère OffsetDateTime. S'il y a un nom de fuseau horaire (comme Europe/Londres) et les règles de l'heure d'été, utilisez un ZonedDateTime –

+2

Merci, cela fonctionne très bien. –