web-dev-qa-db-fra.com

Java 8 format LocalDate Jackson

Pour Java.util.Date quand je le fais

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")  
  private Date dateOfBirth;

puis dans la demande JSON lorsque j'envoie

{ {"dateOfBirth":"01/01/2000"} }  

ça marche.

Comment dois-je faire cela pour champ LocalDate de Java 8 ??

J'ai essayé d'avoir

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;  

Ça n'a pas marché.

Quelqu'un peut-il s'il vous plaît laissez-moi savoir quelle est la bonne façon de faire cela ..

Ci-dessous les dépendances

<dependency>
         <groupId>org.jboss.resteasy</groupId>
         <artifactId>jaxrs-api</artifactId>
         <version>3.0.9.Final</version>
      </dependency>
      <dependency>
         <groupId>com.fasterxml.jackson.jaxrs</groupId>
         <artifactId>jackson-jaxrs-json-provider</artifactId>
         <version>2.4.2</version>
      </dependency>
      <dependency>
         <groupId>com.wordnik</groupId>
         <artifactId>swagger-annotations</artifactId>
         <version>1.3.10</version>
      </dependency>
      <dependency>
101
JAB

Je n'ai jamais réussi à faire fonctionner cela simplement en utilisant des annotations. Pour que cela fonctionne, j'ai créé un ContextResolver pour ObjectMapper, puis j'ai ajouté le JSR310Module, ainsi qu'une mise en garde supplémentaire, à savoir la nécessité de définir write-date -as-timestamp à false. Voir plus la documentation du module JSR310 . Voici un exemple de ce que j'ai utilisé.

Dépendance

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

Remarque: Un problème que j'ai rencontré est que la version jackson-annotation tirée par une autre dépendance, utilisait la version 2.3.2, qui annulait le 2.4 requis par le jsr310. Ce qui s’est passé, c’est que j’ai eu un NoClassDefFound pour ObjectIdResolver, qui est une classe 2.4. Je devais donc simplement aligner les versions de dépendance incluses

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        // Now you should use JavaTimeModule instead
        MAPPER.registerModule(new JSR310Module());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

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

Classe de ressources

@Path("person")
public class LocalDateResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getPerson() {
        Person person = new Person();
        person.birthDate = LocalDate.now();
        return Response.ok(person).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createPerson(Person person) {
        return Response.ok(
                DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
    }

    public static class Person {
        public LocalDate birthDate;
    }
}

Tester

curl -v http://localhost:8080/api/person
Résultat: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person
Résultat: 2015-03-01


Voir aussi ici pour la solution JAXB.

MISE À JOUR

Le JSR310Module est obsolète à partir de la version 2.7 de Jackson. Au lieu de cela, vous devriez enregistrer le module JavaTimeModule. C'est toujours la même dépendance.

85
Paul Samsotha

@JsonSerialize et @JsonDeserialize ont bien fonctionné pour moi. Ils éliminent le besoin d'importer le module jsr310 supplémentaire:

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;

Désérialiseur:

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    protected LocalDateDeserializer() {
        super(LocalDate.class);
    }


    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        return LocalDate.parse(jp.readValueAs(String.class));
    }

}

Sérialiseur:

public class LocalDateSerializer extends StdSerializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    public LocalDateSerializer(){
        super(LocalDate.class);
    }

    @Override
    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider sp) throws IOException, JsonProcessingException {
        gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
    }
}
73
Christopher Yang
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

fonctionne bien pour moi.

45
欧阳世雄

Au printemps, application Web d'amorçage, avec les versions "jackson" et "jsr310" "2.8.5"

compile "com.fasterxml.jackson.core:jackson-databind:2.8.5"
runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.5"

Le '@JsonFormat' fonctionne:

import com.fasterxml.jackson.annotation.JsonFormat;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate birthDate;
37
Tsolak Barseghyan

La solution la plus simple (qui prend également en charge la désérialisation et la sérialisation) est

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate dateOfBirth;

Tout en utilisant les dépendances suivantes dans votre projet.

Maven

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.9.7</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.datatype</groupId>
   <artifactId>jackson-datatype-jsr310</artifactId>
   <version>2.9.7</version>
</dependency>

Gradle

compile "com.fasterxml.jackson.core:jackson-databind:2.9.7"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7"

Aucune implémentation supplémentaire de ContextResolver, Serializer ou Deserializer n'est requise.

17
Paul Wasilewski

Puisque LocalDateSerializer le transforme en "[année, mois, jour]" (un tableau json) plutôt que "année-mois-jour" (une chaîne json) par défaut, et puisque je ne veux pas en demander special ObjectMapper setup (vous pouvez faire LocalDateSerializer générer des chaînes si vous désactivez SerializationFeature.WRITE_DATES_AS_TIMESTAMPS mais cela nécessite une configuration supplémentaire pour votre ObjectMapper), j'utilise ce qui suit:

importations:

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

code:

// generates "yyyy-MM-dd" output
@JsonSerialize(using = ToStringSerializer.class)
// handles "yyyy-MM-dd" input just fine (note: "yyyy-M-d" format will not work)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate localDate;

Et maintenant, je peux simplement utiliser new ObjectMapper() pour lire et écrire mes objets sans configuration particulière.

15
Shadow Man

Juste une mise à jour de la réponse de Christopher.

Depuis la version 2.6.0

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

Utilisez le JavaTimeModule au lieu de JSR310Module (obsolète).

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        MAPPER.registerModule(new JavaTimeModule());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

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

Selon la documentation , le nouveau JavaTimeModule utilise les mêmes paramètres standard pour la sérialisation par défaut, mais n'utilise pas les ID de fuseau horaire, mais utilise uniquement des décalages de fuseau horaire conformes à la norme ISO-8601.

Le comportement peut être modifié en utilisant SerializationFeature.WRITE_DATES_WITH_ZONE_ID

4
bdzzaid

https://stackoverflow.com/a/53251526/1282532 est le moyen le plus simple de sérialiser/désérialiser une propriété. J'ai deux préoccupations concernant cette approche - jusqu'à un certain point, une violation du principe DRY et un couplage élevé entre pojo et mapper.

public class Trade {
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate tradeDate;
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate maturityDate;
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate entryDate;
}

Si vous avez un POJO avec plusieurs champs LocalDate, il est préférable de configurer le mappeur au lieu de POJO. Cela peut être aussi simple que https://stackoverflow.com/a/35062824/1282532 si vous utilisez des valeurs ISO-8601 ("2019-01-31")

Si vous devez gérer un format personnalisé, le code sera le suivant:

ObjectMapper mapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
mapper.registerModule(javaTimeModule);

La logique est écrite une seule fois, elle peut être réutilisée pour plusieurs POJO

1
Pavel

Dans la classe de configuration, définissez LocalDateSerializer et LocalDateDeserializer et enregistrez-les dans ObjectMapper via JavaTimeModule comme ci-dessous:

@Configuration
public class AppConfig
{
@Bean
    public ObjectMapper objectMapper()
    {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_EMPTY);
        //other mapper configs
        // Customize de-serialization


        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        mapper.registerModule(javaTimeModule);

        return mapper;
    }

    public class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(Constant.DATE_TIME_FORMATTER));
        }
    }

    public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {

        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return LocalDate.parse(p.getValueAsString(), Constant.DATE_TIME_FORMATTER);
        }
    }
}
0
nanosoft