web-dev-qa-db-fra.com

Spring Boot a des problèmes avec la sérialisation de Java.time.LocalDateTime avec Jackson pour renvoyer des horodatages JSON ISO-8601?

Je travaille sur la conversion de certains modèles dans une application d'API REST spring-boot pour qu'ils utilisent le code Java.time.LocalDateTime de Java 8 au lieu du paramètre DateTime de joda. Je souhaite que les horodatages renvoyés par l'appel de l'API adhèrent au format ISO_8601. La motivation doit être compatible avec le temps de Java 8 ( plus ici ).

La partie qui s'avère difficile concerne la sérialisation d'un objet contenant LocalDateTime en JSON. 

Par exemple, j'ai l'entité suivante:

// ... misc imports

import Java.time.LocalDateTime;
import Java.time.ZoneOffset;

@Data 
@Entity
@Table(name = "users")
public class User {

    @Id @Column
    private String id;

    @Column
    private String name;

    @Column
    private String email;

    @Column
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "UTC")
    private Java.time.LocalDateTime createdAt;

    public User(String name, String email) {
        this.id = Utils.generateUUID();
        this.createdAt = LocalDateTime.now(ZoneOffset.UTC);
    }
}

J'ai également défini mon application.properties pour désactiver les dates en tant qu'option timestamp jackson:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false

Mon maven deps:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.3.6.RELEASE</version>
</dependency>

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

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-core</artifactId>
     <version>5.0.1.Final</version>
 </dependency>

Enfin, j'essaie de récupérer la représentation JSON via le contrôleur:

@RequestMapping("/users")
@RestController
public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping(
        value = "/{id}",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE
    )
    public User getUser(@PathVariable("id") String id) {
        return userService.findById(id);
    }
}

Lorsque je passe un appel à ce noeud final, j'obtiens l'exception suivante:

Java.lang.NoSuchMethodError: 
com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase.findFormatOverrides(Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/BeanProperty;Ljava/lang/Class;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value;

Alternativement, j'ai également configuré la variable ObjectMapper de l'application dans la classe de configuration:

@Configuration
public class ServiceConfiguration {

    @Bean
    public ObjectMapper getJacksonObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(
            com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
            false
        );
        return objectMapper;
    }
}

Toute piste sera grandement appréciée. 


METTRE À JOUR:

Il s'avère que c'était une incompatibilité de version entre la version Jackson de Spring Boot et celle que j'avais dans mon pom.xml. Comme Miloš et Andy l'ont proposé, une fois que j'ai défini la version correcte et exécuté l'application avec spring.jackson.serialization.write_dates_as_timestamps=true, le problème a été résolu, sans qu'il soit nécessaire de configurer ObjectMapper ni d'ajouter des annotations sur mes champs de modèle LocalDateTime.

    ...
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.3.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.3.6.RELEASE</version>
        </dependency>

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

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>

    </dependencies>
14
sa125

La NoSuchMethodError est due au fait que vous mélangez des versions de Jackson. Spring Boot 1.3.6 utilise Jackson 2.6.7 et vous utilisez 2.8.1 de jackson-datatype-jsr310.

Spring Boot fournit une gestion des dépendances pour Jackson, y compris jackson-datatype-jsr310. Vous devez donc supprimer la version de votre pom. Si vous souhaitez utiliser une version différente de Jackson, vous devez redéfinir la propriété jackson.version:

<properties>
    <jackson.version>2.8.1</jackson.version>
</properties>

Cela garantira que toutes vos dépendances de Jackson auront la même version, évitant ainsi les problèmes de versions incompatibles.

Vous pouvez également, si vous le souhaitez, supprimer votre code Java configurant la variable ObjectMapper. Le module Java Time sera automatiquement enregistré quand il se trouvera dans le chemin de classe et les dates d'écriture car les horodatages peuvent être configurés dans application.properties:

spring.jackson.serialization.write-dates-as-timestamps=false
23
Andy Wilkinson

Votre bean ObjectMapper doit être marqué @Primary pour pouvoir être récupéré avant le printemps. Sinon, vous pouvez simplement créer un bean JavaTimeModule et il sera sélectionné par Spring et ajouté au mappeur d'objets par défaut.

Vous l'avez probablement déjà vu mais jetez un coup d'œil à la documentation officielle .

5
Miloš Milivojević

L'erreur se produit parce que vous mélangez des versions de Jackson. Vous utilisez la version 1.3.6.RELEASE de Spring Boot. Si vous souhaitez migrer vers la version Spring de démarrage 2.x.x.RELEASE, vous pouvez remplacer la dépendance com.fasterxml.jackson.datatype par une dépendance spring-boot-starter-json. De cette façon, vous laissez Spring Boot s’occuper de la bonne version de Jackson.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
    </dependency>
1
Paul Rambags