Jackson 自定义即时从 ISO8601 字符串反序列化,不带 Z

发布于 2025-01-10 22:30:31 字数 6212 浏览 0 评论 0原文

我需要将 ISO 8601 值反序列化为 Instant,但该字符串没有尾随 Z 字符。

例如,

2022-03-01T15:42:23.800

而不是

2022-03-01T15:42:23.800Z

这些是我的测试:

public class JacksonParsingTests {

private static ObjectMapper objectMapper;

@BeforeClass
public static void setUp(){
    objectMapper = new ObjectMapper();

    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule());
}

@Test
public void testInstantSerializationFormat(){
    try {
        String instantJSON = objectMapper.writeValueAsString(Instant.now());

        System.out.println("Instant JSON:");
        System.out.println(instantJSON);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
}

@Test
public void testInstantDeserialization() {

    try {
        String iso8601JSON = "\"2022-03-01T15:42:23.800Z\"";
        Instant now = objectMapper.readValue(iso8601JSON, Instant.class);

        System.out.println("Zulu ISO8601 deserialization:");
        System.out.println(now);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void testCustomInstantDeserialization(){
    try {
        String customISO8601JSON = "\"2022-03-01T15:42:23.800\"";
        Instant now = objectMapper.readValue(customISO8601JSON, Instant.class);

        System.out.println("No Zulu ISO8601 deserialization:");
        System.out.println(now);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

如何为 Instant 类定义自定义反序列化器并将其注册到 ObjectMapper?

我尝试过

public class ISO8601InstantDeserializer extends InstantDeserializer<Instant> {
    public ISO8601InstantDeserializer() {
        super(InstantDeserializer.INSTANT, new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
            .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
            .optionalStart().appendOffset("+HH", "Z").optionalEnd()
            .toFormatter());
    }
}

@BeforeClass
public static void setUp(){
    objectMapper = new ObjectMapper();

    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule().addDeserializer(Instant.class, new ISO8601InstantDeserializer()));
}

没有成功。

我得到的错误是:

com.fasterxml.jackson.databind.JsonMappingException: Failed to deserialize java.time.Instant: (java.time.DateTimeException) Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 2022-03-01T15:42:23.800 of type java.time.format.Parsed
 at [Source: "2022-03-01T15:42:23.800"; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277)
    at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._rethrowDateTimeException(JSR310DeserializerBase.java:82)
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:208)
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:48)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2858)
    at eu.config.jackson.JacksonParsingTests.testCustomInstantDeserialization(JacksonParsingTests.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    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:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 2022-03-01T15:42:23.800 of type java.time.format.Parsed
    at java.time.Instant.from(Instant.java:378)
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:203)
    ... 27 more
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds
    at java.time.format.Parsed.getLong(Parsed.java:203)
    at java.time.Instant.from(Instant.java:373)
    ... 28 more

I need to deserialize a ISO 8601 value into an Instant, but the string is without the trailing Z character.

E.g.

2022-03-01T15:42:23.800

instead of

2022-03-01T15:42:23.800Z

These are my tests:

public class JacksonParsingTests {

private static ObjectMapper objectMapper;

@BeforeClass
public static void setUp(){
    objectMapper = new ObjectMapper();

    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule());
}

@Test
public void testInstantSerializationFormat(){
    try {
        String instantJSON = objectMapper.writeValueAsString(Instant.now());

        System.out.println("Instant JSON:");
        System.out.println(instantJSON);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
}

@Test
public void testInstantDeserialization() {

    try {
        String iso8601JSON = "\"2022-03-01T15:42:23.800Z\"";
        Instant now = objectMapper.readValue(iso8601JSON, Instant.class);

        System.out.println("Zulu ISO8601 deserialization:");
        System.out.println(now);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void testCustomInstantDeserialization(){
    try {
        String customISO8601JSON = "\"2022-03-01T15:42:23.800\"";
        Instant now = objectMapper.readValue(customISO8601JSON, Instant.class);

        System.out.println("No Zulu ISO8601 deserialization:");
        System.out.println(now);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

How can I define a custom deserializer for the Instant class and register it with the ObjectMapper?

I tried with

public class ISO8601InstantDeserializer extends InstantDeserializer<Instant> {
    public ISO8601InstantDeserializer() {
        super(InstantDeserializer.INSTANT, new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
            .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
            .optionalStart().appendOffset("+HH", "Z").optionalEnd()
            .toFormatter());
    }
}

and

@BeforeClass
public static void setUp(){
    objectMapper = new ObjectMapper();

    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule().addDeserializer(Instant.class, new ISO8601InstantDeserializer()));
}

but without success.

The error I get is:

com.fasterxml.jackson.databind.JsonMappingException: Failed to deserialize java.time.Instant: (java.time.DateTimeException) Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 2022-03-01T15:42:23.800 of type java.time.format.Parsed
 at [Source: "2022-03-01T15:42:23.800"; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277)
    at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._rethrowDateTimeException(JSR310DeserializerBase.java:82)
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:208)
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:48)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2858)
    at eu.config.jackson.JacksonParsingTests.testCustomInstantDeserialization(JacksonParsingTests.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    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:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 2022-03-01T15:42:23.800 of type java.time.format.Parsed
    at java.time.Instant.from(Instant.java:378)
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:203)
    ... 27 more
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds
    at java.time.format.Parsed.getLong(Parsed.java:203)
    at java.time.Instant.from(Instant.java:373)
    ... 28 more

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文