【问题标题】:Jackson not parsing Timestamp correctly杰克逊没有正确解析时间戳
【发布时间】:2017-09-22 06:53:39
【问题描述】:

我在使用 Jackson 2.8.5 从 XML 文件中解析 java.sql.Timestamp 时遇到问题。不知何故,毫秒在左侧用零填充。

这是一个最小的例子:

public class Foo {

    @JacksonXmlProperty(localName = "ts")
    Timestamp ts;

    public static void main(String[] args) throws IOException {
        String xml = "<Foo><ts>2017-09-21T11:25:32.1Z</ts></Foo>"
        Foo foo = new XmlMapper().readValue(xml, Foo.class);
        System.out.println("foo.ts = " + foo.ts);
    }
}

foo.ts = 2017-09-21 11:25:32.001

而如果我手动解析字符串,我会得到预期值

System.out.println(Instant.parse("2017-09-21T11:25:32.1Z"));

2017-09-21 11:25:32.1

【问题讨论】:

    标签: java jackson timestamp datetime-parsing jackson-dataformat-xml


    【解决方案1】:

    这似乎是SimpleDateFormat(杰克逊内部使用)的问题,如stated in this answer。我也用纯Java(没有Jackson)做了一个测试,错误也发生了:

    String s = "2017-09-21T11:25:32.1Z";
    Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SX").parse(s);
    System.out.println(new Timestamp(date.getTime())); // 2017-09-21 08:25:32.001
    

    如果您可以更改 xml,则向输入 (2017-09-21T11:25:32.100Z) 添加零即可。


    另一种选择是为您的字段编写自定义反序列化程序(使用Timestamp 类中的正确方法从Instant 转换):

    public class CustomTimestampDeserializer extends JsonDeserializer<Timestamp> {
    
        @Override
        public Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return Timestamp.from(Instant.parse(p.getText()));
        }
    }
    

    然后您注释该字段以使用此自定义反序列化器:

    @JacksonXmlProperty(localName = "ts")
    @JsonDeserialize(using = CustomTimestampDeserializer.class)
    Timestamp ts;
    

    现在,当打印 Timestamp 时,您会得到正确的值:

    2017-09-21 08:25:32.1

    等一下,为什么小时是08 - 那是因为当你System.out.printlnTimestamp 时,它隐式调用了toString() 方法,而这个方法将@ 987654338@ 到 JVM 默认时区(在我的情况下,它是 America/Sao_Paulo,所以它是正确的,因为圣保罗的 08:25 AM 相当于 UTC 的 11:25 AM)。但是Timestamp保存的值是正确的。

    您可以在this article 中阅读更多关于toString() 行为的信息——它谈到了java.util.Date,但想法是一样的(特别是因为Timestamp 扩展了Date,所以它有同样的问题)。


    要将其序列化回 xml,您还可以配置自定义序列化程序:

    public class CustomTimestampSerializer extends JsonSerializer<Timestamp> {
    
        @Override
        public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            gen.writeString(value.toInstant().toString());
        }
    }
    

    然后你注释字段以使用这个序列化器:

    @JacksonXmlProperty(localName = "ts")
    @JsonDeserialize(using = CustomTimestampDeserializer.class)
    @JsonSerialize(using = CustomTimestampSerializer.class)
    Timestamp ts;
    

    只是一个细节:Instant.toString() 将导致2017-09-21T11:25:32.100Z。如果您最后不想要那些额外的零,您可以使用DateTimeFormatter 自定义格式。所以自定义的序列化器会是这样的:

    public class CustomTimestampSerializer extends JsonSerializer<Timestamp> {
    
        private DateTimeFormatter fmt = new DateTimeFormatterBuilder()
            // date/time
            .appendPattern("yyyy-MM-dd'T'HH:mm:ss")
            // nanoseconds without leading zeroes (from 0 to 9 digits)
            .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
            // offset (Z)
            .appendOffsetId()
            // create formatter (set zone to UTC to properly format the Instant)
            .toFormatter().withZone(ZoneOffset.UTC);
    
        @Override
        public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            gen.writeString(fmt.format(value.toInstant()));
        }
    }
    

    这会将时间戳打印为2017-09-21T11:25:32.1Z

    【讨论】:

    • 感谢您的详尽回答!我知道使用自定义(反)序列化程序的替代方法,但我不想那样做。我也对我的问题撒了谎,它实际上是在打印 2017-09-21 09:25:32.1 而不是 2017-09-21 11:25:32.1 但我知道时区问题所以我伪造了它;-) 我认为这可能是一个错误,我已经打开了一个问题 @987654323 @.
    • @cheseaux 显然,SimpleDateFormat(jackson 在内部使用)似乎有问题:stackoverflow.com/a/7160014/7605325 - 我刚刚用纯 Java(没有 jackson)进行了测试,它有一样的问题。无论如何,我已经更新了答案,添加了这个信息
    • @cheseaux 我在 Oracle 和 they accepted 中打开了一个问题,这强烈表明问题确实出在 SimpleDateFormat 类(杰克逊内部使用)中。在这种情况下,最好的选择可能是使用自定义(反)序列化程序。
    猜你喜欢
    • 2014-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-25
    • 2019-07-27
    • 2014-08-22
    • 2019-11-25
    • 1970-01-01
    相关资源
    最近更新 更多