Comment sérialiser Joda DateTime avec le processeur Jackson JSON?

118

Comment faire pour que Jackson sérialise mon objet Joda DateTime selon un modèle simple (comme "jj-MM-aaaa")?

J'ai essayé:

@JsonSerialize(using=DateTimeSerializer.class)
private final DateTime date;

J'ai aussi essayé:

ObjectMapper mapper = new ObjectMapper()
    .getSerializationConfig()
    .setDateFormat(df);

Merci!

Haywood Jablomey
la source
Les deux éléments ci-dessus devraient également fonctionner (@JsonSerialize devrait impliquer que le champ doit être sérialisé; et le format de date devrait également s'appliquer idéalement à Joda), vous pouvez donc déposer un bogue Jira sur jira.codehaus.org/browse/JACKSON .
StaxMan
Je me rends compte que cette question remonte à un certain temps, mais pour référence future, objectMapper.getSerializationConfig (). SetDateFormat (df) est maintenant obsolète. objectMapper.setDateFormat (df) est maintenant suggéré.
Patrick

Réponses:

146

C'est devenu très simple avec Jackson 2.0 et le module Joda.

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

Dépendance Maven:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId>
  <version>2.1.1</version>
</dependency>  

Code et documentation: https://github.com/FasterXML/jackson-datatype-joda

Binaires: http://repo1.maven.org/maven2/com/fasterxml/jackson/datatype/jackson-datatype-joda/

Kimble
la source
13
Pourriez-vous développer cela s'il vous plaît? Où va ce code et comment l'instance ObjectMapper est-elle utilisée?
Paul
Vous devriez poser des questions spécifiques concernant l'utilisation de l'API de Jackson et de Maven. La destination du code dépend de l'utilisation de frameworks / de la façon dont vous amorcez votre application.
Kimble
4
quand j'ajoute que j'obtiens l'erreur de compilation "types incompatibles: JodaModule ne peut pas être converti en module" - la méthode attend un org.codehaus.jackson.map.Module mais JodaModule ne l'a pas dans son héritage alors comment cela pourrait-il fonctionner?
Martin Charlesworth
4
Prometteur mais actuellement presque inutile car nous ne pouvons pas spécifier le format de date à analyser :(
Vincent Mimoun-Prat
10
Vous devez désactiver la sérialisation en tant qu'horodatages objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
MarekM
74

Dans l'objet que vous mappez:

@JsonSerialize(using = CustomDateSerializer.class)
public DateTime getDate() { ... }

Dans CustomDateSerializer:

public class CustomDateSerializer extends JsonSerializer<DateTime> {

    private static DateTimeFormatter formatter = 
        DateTimeFormat.forPattern("dd-MM-yyyy");

    @Override
    public void serialize(DateTime value, JsonGenerator gen, 
                          SerializerProvider arg2)
        throws IOException, JsonProcessingException {

        gen.writeString(formatter.print(value));
    }
}
Rusty Kuntz
la source
4
Comment enregistrer CustomDateSerializer dans Spring MVC 3?
digz6666
1
Je ne connais pas Spring MVC3, mais vous pouvez ajouter un sérialiseur personnalisé à l'objet mappeur Jackson. Voir cette entrée dans Jackson Vous devez créer un module simple pour ce SimpleModule isoDateTimeModule = new SimpleModule ("ISODateTimeModule", nouvelle version (1, 0, 0, null)); isoDateTimeModule.addSerializer (nouveau JsonISODateTimeFormatSerializer ()); mapper.registerModule (isoDateTimeModule);
Guillaume Belrose le
1
@ digz6666 voir ma réponse ici pour Spring MVC 3: stackoverflow.com/questions/7854030/…
Aram Kocharyan
Remarque - assurez-vous d'utiliser les classes ci-dessus de org.codehaus.jacksonplutôt que com.fasterxml.jackson.core. L'utilisation du deuxième package n'a pas fonctionné pour moi. En fait, mon application Jersey n'a même pas respecté l' @JsonSerializedannotation.
Kevin Meredith
La réponse a fonctionné pour moi. Mais il faudrait cette annotation sur tous les champs. Y a-t-il une configuration globale pour cela qui fonctionne avec Jackson?
Taher
26

Comme @Kimble l'a dit, avec Jackson 2, l'utilisation du formatage par défaut est très simple; inscrivez- JodaModulevous simplement sur votre ObjectMapper.

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

Pour une sérialisation / désérialisation personnalisée de DateTime, vous devez implémenter votre propre StdScalarSerializeret StdScalarDeserializer; c'est assez compliqué, mais de toute façon.

Par exemple, voici un DateTimesérialiseur qui utilise le ISODateFormatavec le fuseau horaire UTC:

public class DateTimeSerializer extends StdScalarSerializer<DateTime> {

    public DateTimeSerializer() {
        super(DateTime.class);
    }

    @Override
    public void serialize(DateTime dateTime,
                          JsonGenerator jsonGenerator,
                          SerializerProvider provider) throws IOException, JsonGenerationException {
        String dateTimeAsString = ISODateTimeFormat.withZoneUTC().print(dateTime);
        jsonGenerator.writeString(dateTimeAsString);
    }
}

Et le désérialiseur correspondant:

public class DateTimeDesrializer extends StdScalarDeserializer<DateTime> {

    public DateTimeDesrializer() {
        super(DateTime.class);
    }

    @Override
    public DateTime deserialize(JsonParser jsonParser,
                                DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        try {
            JsonToken currentToken = jsonParser.getCurrentToken();
            if (currentToken == JsonToken.VALUE_STRING) {
                String dateTimeAsString = jsonParser.getText().trim();
                return ISODateTimeFormat.withZoneUTC().parseDateTime(dateTimeAsString);
            }
        } finally {
            throw deserializationContext.mappingException(getValueClass());
        }
    }

Ensuite, attachez-les avec un module:

public class DateTimeModule extends SimpleModule {

    public DateTimeModule() {
        super();
        addSerializer(DateTime.class, new DateTimeSerializer());
        addDeserializer(DateTime.class, new DateTimeDeserializer());
    }
}

Enregistrez ensuite le module sur votre ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new DateTimeModule());
oggmonster
la source
l'objet DatatIme deserilazed est dans le fuseau horaire UTC ou dans le fuseau horaire des navigateurs, aidez-moi s'il vous plaît
user3354457
Cela enregistre-t-il globalement le mappeur (et les modules)?
raffian
16

La solution simple

J'ai rencontré un problème similaire et ma solution est beaucoup plus claire que ci-dessus.

J'ai simplement utilisé le motif en @JsonFormat annotation

En gros, ma classe a un DateTimechamp, donc j'ai mis une annotation autour du getter:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public DateTime getDate() {
    return date;
}

Je sérialise la classe avec ObjectMapper

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JodaModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    ObjectWriter ow = mapper.writer();
    try {
        String logStr = ow.writeValueAsString(log);
        outLogger.info(logStr);
    } catch (IOException e) {
        logger.warn("JSON mapping exception", e);
    }

Nous utilisons Jackson 2.5.4

Atais
la source
15

https://stackoverflow.com/a/10835114/1113510

Bien que vous puissiez mettre une annotation pour chaque champ de date, il est préférable de faire une configuration globale pour votre mappeur d'objets. Si vous utilisez jackson, vous pouvez configurer votre ressort comme suit:

<bean id="jacksonObjectMapper" class="com.company.CustomObjectMapper" />

<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
    factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" >
</bean>

Pour CustomObjectMapper:

public class CustomObjectMapper extends ObjectMapper {

    public CustomObjectMapper() {
        super();
        configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
        setDateFormat(new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZZ (z)"));
    }
}

Bien sûr, SimpleDateFormat peut utiliser n'importe quel format dont vous avez besoin.

Moesio
la source
4
setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"));a fait l'affaire pour moi,
merci
8

Pendant ce temps, Jackson enregistre automatiquement le module Joda lorsque le JodaModule est dans classpath. Je viens d'ajouter jackson-datatype-joda à Maven et cela a fonctionné instantanément.

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId>
  <version>2.8.7</version>
</dependency>

Sortie JSON:

{"created" : "2017-03-28T05:59:27.258Z"}
Stéphan
la source
6

Pour ceux qui ont Spring Boot, vous devez ajouter le module à votre contexte et il sera ajouté à votre configuration comme ceci.

@Bean
public Module jodaTimeModule() {
    return new JodaModule();
}

Et si vous souhaitez utiliser le nouveau module de temps java8 jsr-310.

@Bean
public Module jodaTimeModule() {
    return new JavaTimeModule();
}
jgeerts
la source
3

Il semble que pour Jackson 1.9.12, une telle possibilité n'existe pas par défaut, à cause de:

public final static class DateTimeSerializer
    extends JodaSerializer<DateTime>
{
    public DateTimeSerializer() { super(DateTime.class); }

    @Override
    public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonGenerationException
    {
        if (provider.isEnabled(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS)) {
            jgen.writeNumber(value.getMillis());
        } else {
            jgen.writeString(value.toString());
        }
    }

    @Override
    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint)
    {
        return createSchemaNode(provider.isEnabled(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS)
                ? "number" : "string", true);
    }
}

Cette classe sérialise les données à l'aide de la méthode toString () de Joda DateTime.

L'approche proposée par Rusty Kuntz fonctionne parfaitement pour mon cas.

ydrozhdzhal
la source
Vous pouvez toujours enregistrer un sérialiseur et un désérialiseur personnalisés, et ceux-ci auront la priorité sur l'implémentation par défaut.
StaxMan
2

J'utilise Java 8 et cela a fonctionné pour moi.

Ajouter la dépendance sur pom.xml

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>

et ajoutez JodaModule sur votre ObjectMapper

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
Luciana Campello
la source