web-dev-qa-db-fra.com

sérialiser/désérialiser Java 8 Java.time avec le mappeur Jackson JSON

Comment utiliser le mappeur Jackson JSON avec Java 8 LocalDateTime?

org.codehaus.jackson.map.JsonMappingException: impossible d'instancier la valeur de type [type simple, classe Java.time.LocalDateTime] à partir de JSON String; pas de méthode constructeur/chaîne à chaîne unique (via la chaîne de référence: MyDTO ["field1"] -> SubDTO ["date"])

169
Alexander Taylor

Il n'est pas nécessaire d'utiliser des sérialiseurs/désérialiseurs personnalisés ici. Utilisez le module datetime de jackson-modules-Java8 :

Module de type de données permettant à Jackson de reconnaître les types de données de l'API Date et heure de Java 8 (JSR-310).

Ce module ajoute le support pour pas mal de classes:

  • Durée
  • Instant
  • Heure locale
  • Date locale
  • Heure locale
  • Mois
  • OffsetDateTime
  • Décalage horaire
  • Période
  • Année
  • Année mois
  • ZonedDateTime
  • ZoneId
  • ZoneOffset
204
Matt Ball

Mise à jour: laisser cette réponse pour des raisons historiques, mais je ne le recommande pas. Veuillez voir la réponse acceptée ci-dessus.

Dites à Jackson de mapper à l'aide de vos classes de sérialisation [de] personnalisées:

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime ignoreUntil;

fournir des classes personnalisées:

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
        arg1.writeString(arg0.toString());
    }
}

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {
        return LocalDateTime.parse(arg0.getText());
    }
}

fait aléatoire: si je niche au-dessus des classes et ne les rend pas statiques, le message d'erreur est étrange: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported

57
Alexander Taylor

Si vous utilisez la classe ObjectMapper de plus rapide, Par défaut, ObjectMapper ne comprend pas la classe LocalDateTime, vous devez donc ajouter une autre dépendance à votre classe/maven: 

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3'

Vous devez maintenant enregistrer le support de types de données offert par cette bibliothèque dans votre objet objectmapper. Pour ce faire, procédez comme suit: 

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();

Maintenant, dans votre jsonString, vous pouvez facilement insérer votre champ Java.LocalDateTime comme suit:

{
    "user_id": 1,
    "score": 9,
    "date_time": "2016-05-28T17:39:44.937"
}

En procédant ainsi, la conversion de votre fichier Json en objet Java fonctionnera correctement. Vous pouvez le lire en procédant comme suit: 

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
            });
39
greperror

Cette dépendance maven va résoudre votre problème:

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

Une chose que j'ai eu du mal à faire est que pour le fuseau horaire ZonedDateTime qui a été changé en GMT pendant la désérialisation….

Jackson2ObjectMapperBuilder.json()
    .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
37
iTake

J'ai eu un problème similaire lors de l'utilisation de Spring Boot . Avec Spring Boot 1.5.1.RELEASE, tout ce que j'avais à faire est d'ajouter une dépendance:

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

Si vous utilisez Jersey, vous devez ajouter la dépendance Maven (jackson-datatype-jsr310) comme les autres suggestions et enregistrer votre instance de mappeur d'objets de la manière suivante:

@Provider
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {

  final ObjectMapper defaultObjectMapper;

  public JacksonObjectMapper() {
    defaultObjectMapper = createDefaultMapper();
  }

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

  private static ObjectMapper createDefaultMapper() {
    final ObjectMapper mapper = new ObjectMapper();    
    mapper.registerModule(new JavaTimeModule());
    return mapper;
  }
}

Lorsque vous enregistrez Jackson dans vos ressources, vous devez ajouter ce mappeur comme suit:

final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
  .register(JacksonObjectMapper.class)
  .register(JacksonJaxbJsonProvider.class);
5
Sul Aga

Si vous ne pouvez pas utiliser jackson-modules-Java8 pour une raison quelconque, vous pouvez (dés) sérialiser le champ instant tant que vous utilisez @JsonIgnore et @JsonGetter & @JsonSetter:

public class MyBean {

    private Instant time = Instant.now();

    @JsonIgnore
    public Instant getTime() {
        return this.time;
    }

    public void setTime(Instant time) {
        this.time = time;
    }

    @JsonGetter
    private long getEpochTime() {
        return this.time.toEpochMilli();
    }

    @JsonSetter
    private void setEpochTime(long time) {
        this.time = Instant.ofEpochMilli(time);
    }
}

Exemple:

@Test
public void testJsonTime() throws Exception {
    String json = new ObjectMapper().writeValueAsString(new MyBean());
    System.out.println(json);
    MyBean myBean = new ObjectMapper().readValue(json, MyBean.class);
    System.out.println(myBean.getTime());
}

les rendements

{"epochTime":1506432517242}
2017-09-26T13:28:37.242Z
3
Udo

Si quelqu'un rencontre un problème lors de l'utilisation de SpringBoot, voici comment j'ai résolu le problème sans ajouter de nouvelle dépendance.

Dans Spring 2.1.3, Jackson s'attend à ce que la chaîne de date 2019-05-21T07:37:11.000 de ce format yyyy-MM-dd HH:mm:ss.SSS soit désérialisée en LocalDateTime. Assurez-vous que la chaîne de date sépare la date et l'heure avec T pas avec space. secondes (ss) et millisecondes (SSS) peuvent être omis.

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
1
Muhammad Usman

J'utilise ce format de temps: "{birthDate": "2018-05-24T13:56:13Z}" pour désérialiser de json en Java.time.Instant (voir capture d'écran)

 enter image description here

1
Taras Melnyk

Pour ceux qui utilisent Spring Boot 2.x 

Aucune des opérations ci-dessus n’est nécessaire: Java 8 LocalDateTime est sérialisé/désérialisé par défaut. Je devais faire tout ce qui précède dans 1.x, mais avec Boot 2.x, cela fonctionne de manière transparente. 

Voir aussi cette référence - Format JSON Java 8 LocalDateTime dans Spring Boot

0
HopeKing

Vous pouvez définir ceci dans votre fichier application.yml pour résoudre Instant Time, qui est Date API in Java8:

spring.jackson.serialization.write-dates-as-timestamps=false
0
Janki

Si vous utilisez Spring Boot et avez ce problème avec OffsetDateTime, vous devez utiliser les registerModules comme indiqué ci-dessus par @greperror (répondu le 28 mai 16 à 13:04), mais notez qu'il existe une différence. La dépendance mentionnée n'a pas besoin d'être ajoutée car je suppose que cette botte de printemps l'a déjà. J'avais ce problème avec le démarrage de printemps et cela a fonctionné pour moi sans ajouter cette dépendance.

0
VC2019

Si vous envisagez d’utiliser fastjson, vous pouvez résoudre votre problème en notant la version.

 <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.56</version>
 </dependency>
0
forever LA