背景
{
"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.
}
問題
アプリケーションがメッセージをオブジェクトに逆シリアル化しようとすると、次の例外がスローされます
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
現在のコード
次のobjectMapperを定義しています
/**
* 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;
}
質問
POJOの結果のZonedDateTimeには、ソースメッセージには存在しない「time」要素が必要であることを理解しています。私はobjectMapperのみを制御できます。これを機能させる可能性のある構成はありますか?
注
逆シリアル化されたPOJOの時間要素がstartOfDayであると「想定」されている場合、つまり「00.00.00.000Z」であれば問題ありません。
@ cassiomolin に示すように、独自のデシリアライザを作成できます。しかし、別のオプションもあります。スタックトレースにはDeserializationContext.weirdStringException
メソッドを使用して、奇妙な文字列値の処理をDeserializationProblemHandler
に提供できます。以下の例を参照してください:
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);
}
}
上記のコードプリント:
Markdown{startDate=2019-07-25T00:00-07:00[America/Los_Angeles]}
String
変数startDate
をZonedDateTimne
に変換する必要があります。変換するとDBに保存されます。
来るjsonのstartDateは文字列形式であり、POJOを変更できないと述べたので、private ZonedDateTime startDate;
に割り当てる前に変換する必要があります。
以下の例のように行うことができます。
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);
悲しいことに、POJOのタイプをLocalDateに変更しないと、それは難しいでしょう。
私が考えることができる最も近い解決策は、ジャクソンのカスタムJsonDeserializer
を記述することです。