Contexte
{
"markdownPercentage": 20,
"currency": "SEK",
"startDate": "2019-07-25"
}
public class Markdown {
@JsonProperty("markdownPercentage")
@NotNull
private Integer markdownPercentage = 0;
@JsonProperty("currency")
@NotNull
private String currency = "";
@JsonFormat(
shape = Shape.STRING,
pattern = "yyyy-MM-dd"
)
@JsonProperty("startDate")
@NotNull
private ZonedDateTime startDate;
// Constructors, Getters, Setters etc.
}
Problème
Lorsque l'application tente de désérialiser le message vers l'objet, elle lève l'exception suivante
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `Java.time.ZonedDateTime` from String "2019-07-25": Failed to deserialize Java.time.ZonedDateTime: (Java.time.DateTimeException) Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2019-07-25 of type Java.time.format.Parsed
at [Source: (String)"{"styleOption":"so2_GreyMelange_1563966403695_1361997740","markdowns":[{"markdownPercentage":20,"currency":"SEK","startDate":"2019-07-25"},{"markdownPercentage":20,"currency":"NOK","startDate":"2019-07-25"},{"markdownPercentage":20,"currency":"CHF","startDate":"2019-07-25"}]}"; line: 1, column: 126] (through reference chain: com.bestseller.generated.interfacecontracts.kafkamessages.pojos.markdownScheduled.MarkdownScheduled["markdowns"]->Java.util.ArrayList[0]->com.bestseller.generated.interfacecontracts.kafkamessages.pojos.markdownScheduled.Markdown["startDate"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.Java:67)
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.Java:1549)
at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.Java:911)
at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.Java:80)
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.Java:212)
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.Java:50)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.Java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.Java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.Java:151)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:286)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:245)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:27)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.Java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.Java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.Java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.Java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.Java:3004)
at com.bestseller.mps.functional.TestingConfiguration.test(TestingConfiguration.Java:42)
at Java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
at Java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.base/Java.lang.reflect.Method.invoke(Method.Java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.Java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.Java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.Java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.Java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.Java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.Java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.Java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.Java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.Java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:70)
Caused by: Java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2019-07-25 of type Java.time.format.Parsed
at Java.base/Java.time.ZonedDateTime.from(ZonedDateTime.Java:566)
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.Java:207)
... 35 more
Caused by: Java.time.DateTimeException: Unable to obtain ZoneId from TemporalAccessor: {},ISO resolved to 2019-07-25 of type Java.time.format.Parsed
at Java.base/Java.time.ZoneId.from(ZoneId.Java:463)
at Java.base/Java.time.ZonedDateTime.from(ZonedDateTime.Java:554)
... 36 more
Code actuel
J'ai le objectMapper suivant défini
/**
* Date mapper.
*
* @return the {@link ObjectMapper}
*/
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return mapper;
}
Question
Je comprends que le ZonedDateTime résultant dans le POJO a besoin d'un élément "time" qui n'est pas présent dans le message source. Je contrôle uniquement ObjectMapper. Y a-t-il une configuration possible qui puisse faire fonctionner cela?
Remarque
Je vais bien si l'élément de temps dans le POJO désérialisé est "supposé" être startOfDay, c'est-à-dire "00.00.00.000Z"
Vous pouvez écrire votre propre désérialiseur comme indiqué dans la réponse @ cassiomolin . Mais il y a aussi une autre option. Sur la trace de la pile, nous avons DeserializationContext.weirdStringException
méthode qui nous permet de fournir à nos DeserializationProblemHandler
la gestion des valeurs de chaînes étranges. Voir l'exemple ci-dessous:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import Java.io.IOException;
import Java.time.LocalDate;
import Java.time.ZonedDateTime;
import Java.time.format.DateTimeFormatter;
import Java.util.TimeZone;
public class AppJson {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
// override default time zone if needed
mapper.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
mapper.registerModule(new JavaTimeModule());
mapper.addHandler(new DeserializationProblemHandler() {
@Override
public Object handleWeirdStringValue(DeserializationContext ctxt, Class<?> targetType,
String valueToConvert, String failureMsg) {
LocalDate date = LocalDate.parse(valueToConvert, DateTimeFormatter.ISO_DATE);
return date.atStartOfDay(ctxt.getTimeZone().toZoneId());
}
});
String json = "{\"startDate\": \"2019-07-25\"}";
Markdown markdown = mapper.readValue(json, Markdown.class);
System.out.println(markdown);
}
}
Au-dessus des impressions de code:
Markdown{startDate=2019-07-25T00:00-07:00[America/Los_Angeles]}
Vous devez convertir votre String
variable startDate
en ZonedDateTimne
puis elle sera convertie et enregistrée dans votre base de données ou autre.
Comme dans json startDate à venir est au format chaîne et vous avez dit que vous ne pouvez pas changer le POJO alors, vous devez le convertir avant de l'attribuer à private ZonedDateTime startDate;
Vous pouvez le faire comme dans l'exemple ci-dessous:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss a z");
ZonedDateTime dateTime = ZonedDateTime.parse("2019-03-27 10:15:30 AM +05:30", formatter);
System.out.println(dateTime);
Malheureusement, sans changer le type de POJO en LocalDate, cela serait difficile.
La solution la plus proche à laquelle je peux penser est d'écrire un JsonDeserializer
personnalisé pour Jackson, ce qui n'est absolument pas une bonne pratique pour ce genre de chose.