![](/img/trans.png)
[英]Serialize JSON date format from ZonedDateTime to custom date format using ObjectMapper
[英]Deserialize JSON date format to ZonedDateTime using objectMapper
背景
{
"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 需要一个源消息中不存在的“时间”元素。 我只能控制 objectMapper。 是否有任何可能的配置可以使这项工作?
笔记
如果反序列化的 POJO 中的时间元素“假定”为 startOfDay,即“00.00.00.000Z”,我很好
我只能控制
ObjectMapper
。 是否有任何可能的配置可以使这项工作?
只要您对时间和时区的默认值感到满意,您就可以使用自定义解串器来解决它:
public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {
@Override
public ZonedDateTime deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext)
throws IOException {
LocalDate localDate = LocalDate.parse(
jsonParser.getText(),
DateTimeFormatter.ISO_LOCAL_DATE);
return localDate.atStartOfDay(ZoneOffset.UTC);
}
}
然后将其添加到模块并将该模块注册到您的ObjectMapper
实例:
SimpleModule module = new SimpleModule();
module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
如果将解串器添加到模块不适合您(从某种意义上说,此配置将应用于其他ZonedDateTime
实例),那么您可以依靠混合来定义解串器将应用于哪些字段。 首先定义一个mix-in接口,如下图:
public interface MarkdownMixIn {
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
ZonedDateTime getDate();
}
然后将 mix-in 接口绑定到所需的类:
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Markdown.class, MarkdownMixIn.class);
遗憾的是,如果不将 POJO 的类型更改为 LocalDate,那将很困难。
我能想到的最接近的解决方案是为 Jackson 编写一个自定义的JsonDeserializer
,这对于这种事情绝对不是一个好习惯。
您可以编写自己的反序列化程序,如@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]}
问题:我想将日期从 json 解析为 java LocalDateTime/ZonedDateTime 对象。 ZonedDateTimeSerializer 存在,但 ZonedDateTimeDeserializer 不存在。 因此,我创建了一个自定义 ZonedDateTimeDeserializer。
public static final String ZONED_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSz";
@Getter
@Setter
@JsonSerialize(using = ZonedDateTimeSerializer.class)
@JsonDeserialize(using = ZonedDateTimeDeserializer.class) // Doesn't exist, So I created a custom ZonedDateDeserializer utility class.
@JsonFormat(pattern = ZONED_DATE_TIME_FORMAT)
@JsonProperty("lastUpdated")
private ZonedDateTime lastUpdated;
解决方案:我最终得到了更简单和更少的代码行。
用于反序列化ZonedDateTime的实用程序类:
/**
* Custom {@link ZonedDateTime} deserializer.
*
* @param jsonParser for extracting the date in {@link String} format.
* @param deserializationContext for the process of deserialization a single root-level value.
* @return {@link ZonedDateTime} object of the date.
* @throws IOException throws I/O exceptions.
*/
public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {
@Override
public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
return ZonedDateTime.parse(jsonParser.getText(), DateTimeFormatter.ofPattern(ZONED_DATE_TIME_FORMAT));
}
}
如果您改用LocalDateTime会怎样。 在这种情况下,它更容易,反序列化器和序列化器类已经提供给我们。 不需要上面定义的自定义实用程序类:
@Getter
@Setter
@JsonSerialize(using = LocalDateSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = ZONED_DATE_TIME_FORMAT) //Specify the format you want: "yyyy-MM-dd'T'HH:mm:ss.SSS"
@JsonProperty("created")
private LocalDateTime created;
也有助于研究的其他链接: 导致此解决方案的想法
关键词:json 格式 localDateTime zonedDateTime
不幸的是,默认情况下您不能将String Object
反序列化为ZonedDateTime
格式。 但是您可以通过两种方式克服这个问题。
方式01
在POJO
类ZonedDateTime
类型更改为LocalDate
类型并将值作为字符串传递03-06-2012
方式02
但是如果您必须使用时区存储日期和时间,那么您必须执行以下方法来克服
步骤 01
使用 DateTimeFormat 为ZonedDateTime
反序列化创建一个类
public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {
@Override
public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss z");
LocalDate localDate = LocalDate.parse(p.getText(),dateTimeFormatter);
return localDate.atStartOfDay(ZoneOffset.UTC);
}
}
步骤 02
在@JsonDeserialize
方法级别注释的支持下,您必须将该 Deserialize 类与 POJO 类中的受影响字段一起使用。
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
private ZonedDateTime startDate;
步骤 03
在 ZonedDateTimeDeserializer 类中以上述格式将值作为字符串传递
"startDate" : "09-03-2003 10:15:00 Europe/Paris"
您需要将您的String
变量startDate
转换为ZonedDateTimne
然后它将被转换并保存到您的数据库或其他任何地方。
由于即将到来的 json startDate 是字符串格式,并且您已经说过您不能更改 POJO,您需要在将其分配给private ZonedDateTime startDate;
之前对其进行转换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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.