web-dev-qa-db-fra.com

Comment désérialiser des horodatages en secondes avec Jackson?

J'ai un JSON qui a un horodatage en secondes (c'est-à-dire un horodatage Unix):

{"foo":"bar","timestamp":1386280997}

Demander à Jackson de le désérialiser en un objet avec un champ DateTime pour l'horodatage donne 1970-01-17T01:11:25.983Z, heure peu de temps après l'époque, parce que Jackson suppose qu'il est dans milliseconds . En plus de déchirer le JSON et d'ajouter des zéros, comment pourrais-je faire en sorte que Jackson comprenne les secondes horodatage?

25
Drew Stephens

J'ai écrit un deserializer personnalisé pour gérer les horodatages en secondes (syntaxe Groovy).

class UnixTimestampDeserializer extends JsonDeserializer<DateTime> {
    Logger logger = LoggerFactory.getLogger(UnixTimestampDeserializer.class)

    @Override
    DateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String timestamp = jp.getText().trim()

        try {
            return new DateTime(Long.valueOf(timestamp + '000'))
        } catch (NumberFormatException e) {
            logger.warn('Unable to deserialize timestamp: ' + timestamp, e)
            return null
        }
    }
}

Et puis j'ai annoté mon POGO pour l'utiliser pour l'horodatage:

class TimestampThing {
    @JsonDeserialize(using = UnixTimestampDeserializer.class)
    DateTime timestamp

    @JsonCreator
    public TimestampThing(@JsonProperty('timestamp') DateTime timestamp) {
        this.timestamp = timestamp
    }
}
25
Drew Stephens
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="s")
public Date timestamp;

edit : suggestion de vivek-kothari

@JsonFormat(shape=JsonFormat.Shape.NUMBER, pattern="s")
public Timestamp timestamp;
14
sherpya

Une approche très similaire à celle de @ DrewStephens utilisant l’API Java SE TimeUnit (introduite dans JDK1.5) au lieu de la concaténation simple String et est donc (sans doute) un peu plus propre et plus expressive:

public class UnixTimestampDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser parser, DeserializationContext context) 
            throws IOException, JsonProcessingException {
        String unixTimestamp = parser.getText().trim();
        return new Date(TimeUnit.SECONDS.toMillis(Long.valueOf(unixTimestamp)));
    }
}

Spécification de votre désérialiseur personnalisé (UnixTimestampDeserializer) sur le (s) champ (s) Date concerné (s):

@JsonDeserialize(using = UnixTimestampDeserializer.class)
private Date updatedAt;
8
Priidu Neemre

J'ai eu ce problème exact, sauf que mes objets ZonedDateTime se sont tournés vers unix-timestamps (secondes) et que j'en avais besoin en millisecondes (pour initialiser les classes JS Date sur le navigateur).

L'implémentation d'un sérialiseur/désérialiseur personnalisé semblait trop demander pour quelque chose qui devrait être assez simple. J'ai donc cherché ailleurs et découvert que je ne pouvais que configurer le mappeur d'objets pour le résultat souhaité. 

Parce que mon application remplace déjà le ObjectMapper par défaut fourni par Jersey, je devais simplement désactiver SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS .

Voici mon code

@Provider
public class RPObjectMapperProvider implements ContextResolver<ObjectMapper> {

    final ObjectMapper defaultObjectMapper;

    public RPObjectMapperProvider() {
        defaultObjectMapper = new ObjectMapper();

        // this turned ZonedDateTime objects to proper seconds timestamps 
        defaultObjectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // disable to serialize dates for millis timestamps
        defaultObjectMapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);

        // using Java8 date time classes
        defaultObjectMapper.registerModule(new JavaTimeModule());
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return defaultObjectMapper;
    }
}

Et c'est tout

0
svarog