web-dev-qa-db-fra.com

Désérialiser Java 8 LocalDateTime avec JacksonMapper

J'ai lu plusieurs questions avec des réponses ici dans SO concernant la sérialisation et la désérialisation entre Java.time.LocalDateTime et JSON, mais je n'arrive pas à le faire fonctionner.

J'ai réussi à configurer mon application de démarrage Spring pour renvoyer les dates au format souhaité (YYY-MM-dd HH:mm) mais j’ai des difficultés à accepter les valeurs de ce format en JSON.

Ce sont toutes les choses que j'ai faites jusqu'à présent:

Ajout de la dépendance maven pour jsr310:

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

Spécifié jsr310 dans ma classe principale:

@EntityScan(basePackageClasses = { App.class, Jsr310JpaConverters.class })

Sérialisation désactivée comme horodatage dans application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

Et voici mon mappage d'entités pour datetime:

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

Dans ma base de données, je stocke cette date sous le nom TIMESTAMP au format suivant: 2016-12-01T23:00:00+00:00.

Si j'accède à cette entité via mon contrôleur, il renvoie le JSON avec le format startDate correct. Quand j'essaye de le poster et de le désérialiser, en utilisant YYYY-MM-dd HH:mm _ format, j'obtiens l'exception suivante:

{
  "timestamp": "2016-10-30T14:22:25.285+0000",
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.http.converter.HttpMessageNotReadableException",
  "message": "Could not read document: Can not deserialize value of type Java.time.LocalDateTime from String \"2017-01-01 20:00\": Text '2017-01-01 20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2017, DayOfMonth=1},ISO resolved to 20:00 of type Java.time.format.Parsed\n at [Source: Java.io.PushbackInputStream@679a734d; line: 6, column: 16] (through reference chain: com.gigsterous.api.model.Event[\"startDate\"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type Java.time.LocalDateTime from String \"2017-01-01 20:00\": Text '2017-01-01 20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2017, DayOfMonth=1},ISO resolved to 20:00 of type Java.time.format.Parsed\n at [Source: Java.io.PushbackInputStream@679a734d; line: 6, column: 16] (through reference chain: com.gigsterous.api.model.Event[\"startDate\"])",
  "path": "/api/events"
}

Je sais qu'il existe de nombreuses réponses à ce sujet mais les suivre et essayer pendant quelques heures ne m'a pas aidé à comprendre ce que je faisais mal, donc je serais heureux si quelqu'un pouvait me signaler ce qui me manque. Merci pour toute contribution à ce sujet!

EDIT: Ce sont toutes les classes impliquées dans le processus:

Dépôt:

@Repository
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {
}

Manette:

@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Event> createEvent(@RequestBody Event event) {
        return new ResponseEntity<>(eventRepo.save(event), HttpStatus.CREATED);
}

Ma demande JSON payalod:

{
  "name": "Test",
  "startDate": "2017-01-01 20:00"
}

Un événement:

@Entity
@Table(name = "events")
@Getter
@Setter
public class Event {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "event_id")
    private long id;

    @Column(name = "name")
    private String name;

    @Column(name = "start_date")
    @DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
    @JsonFormat(pattern = "YYYY-MM-dd HH:mm")
    private LocalDateTime startDate;
}
25
Smajl

La date heure que vous passez n'est pas un format de date heure locale iso.

Changer en

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME)
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

et passez la chaîne de date au format '2011-12-03T10: 15: 30'.

Mais si vous voulez toujours passer votre format personnalisé, utilisez simplement le bon formateur.

Changer en

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

Je pense que votre problème est que le @DateTimeFormat n'a aucun effet. Comme la jackson est en train de faire la déseralization et qu’elle ne sait rien de l’annotation printanière, je ne vois pas Spring analyser cette annotation dans le contexte de la désérialisation.

Vous pouvez également essayer de définir le formateur lors de l’enregistrement du module Java time.

LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);

Voici le cas de test avec le déssèralisateur qui fonctionne bien. Peut-être essayez-vous de vous débarrasser complètement de cette annotation DateTimeFormat.

@RunWith(JUnit4.class)
public class JacksonLocalDateTimeTest {

    private ObjectMapper objectMapper;

    @Before
    public void init() {
        JavaTimeModule module = new JavaTimeModule();
        LocalDateTimeDeserializer localDateTimeDeserializer =  new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
        objectMapper = Jackson2ObjectMapperBuilder.json()
                .modules(module)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .build();
    }

    @Test
    public void test() throws IOException {
        final String json = "{ \"date\": \"2016-11-08 12:00\" }";
        final JsonType instance = objectMapper.readValue(json, JsonType.class);

        assertEquals(LocalDateTime.parse("2016-11-08 12:00",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") ), instance.getDate());
    }
}


class JsonType {
    private LocalDateTime date;

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }
}
31
user2683814

Vous avez utilisé une casse de lettre incorrecte pour l'année en cours:

@JsonFormat(pattern = "YYYY-MM-dd HH:mm")

Devrait être:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")

Avec ce changement, tout fonctionne comme prévu.

17
Maciej Walkowiak

Vous pouvez implémenter votre JsonSerializer

Voir:

Que ta propriété en haricot

@JsonProperty("start_date")
@JsonFormat("YYYY-MM-dd HH:mm")
@JsonSerialize(using = DateSerializer.class)
private Date startDate;

De cette façon, implémente votre classe personnalisée

public class DateSerializer extends JsonSerializer<Date> implements ContextualSerializer<Date> {

    private final String format;

    private DateSerializer(final String format) {
        this.format = format;
    }

    public DateSerializer() {
        this.format = null;
    }

    @Override
    public void serialize(final Date value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
        jgen.writeString(new SimpleDateFormat(format).format(value));
    }

    @Override
    public JsonSerializer<Date> createContextual(final SerializationConfig serializationConfig, final BeanProperty beanProperty) throws JsonMappingException {
        final AnnotatedElement annotated = beanProperty.getMember().getAnnotated();
        return new DateSerializer(annotated.getAnnotation(JsonFormat.class).value());
    }

}

Essayez ceci après le résultat du post pour nous.

2
Marcelo Ferreira

MISE À JOUR:

Changer en:

@Column(name = "start_date")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm", iso = ISO.DATE_TIME)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime startDate;

Demande JSON:

{
 "startDate":"2019-04-02 11:45"
}
1
LeandroPL

Cela a fonctionné pour moi:

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;

    @Column(name="end_date", nullable = false)
    @DateTimeFormat(iso = ISO.DATE_TIME)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime endDate;
0
Alferd Nobel