如何在Java中使用Jackson Annotation反序列化时间戳?

时间:2017-09-11 19:40:13

标签: java json datetime jackson utc

我的Java Bean中有一个字段,如下所示@JsonFormat是杰克逊注释:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm")
private Timestamp createdOn;

当我返回Timestamp时,它总是在我的JSON响应中转换为UTC时间。让我们说格式化我的时间戳,我得到2017-09-13 15:30但我的回复是2017-09-13 10:00

我知道这是因为默认情况下Jackson注释采用系统TimeZone并将时间戳转换为UTC。 (在我的情况下,服务器属于Asia/Calcutta时区,因此偏移量为+05:30,Jackson映射器减去5小时30分钟,将时间戳转换为UTC)

有没有办法按原样返回时间戳,即2017-09-13 15:30而不是2017-09-13 10:00

2 个答案:

答案 0 :(得分:4)

我在这里做了一个测试,将JVM默认时区更改为Asia/Calcutta并创建一个带java.sql.Timestamp字段的类:

public class TestTimestamp {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
    private Timestamp createdOn;

    // getter and setter
}

other question中你告诉JDK 8正在使用,所以我在相同的版本中测试过(如果你使用的是另一个版本(JDK <= 7)),请检查“Not Java底部有8“部分)。首先,我创建了一个Timestamp,对应于 2017年9月13日 th ,UTC时间上午10点

TestTimestamp value = new TestTimestamp();
// timestamp corresponds to 2017-09-13T10:00:00 UTC
value.setCreatedOn(Timestamp.from(Instant.parse("2017-09-13T10:00:00Z")));

然后我用杰克逊的ObjectMapper

序列化了它
ObjectMapper om = new ObjectMapper();
String s = om.writeValueAsString(value);

结果String是:

  

{“createdOn”:“2017-09-13 10:00”}

请注意,在生成的JSON中,输出为UTC(上午10点)。如果我反序列化这个JSON:

value = om.readValue(s, TestTimestamp.class);
System.out.println(value.getCreatedOn());

这将打印:

  

2017-09-13 15:30:00.0

这是因为Timestamp::toString()方法(在System.out.println中调用的隐含)在JVM默认时区中打印时间戳。在这种情况下,默认值为Asia/Calcutta 10 AM(UTC )与 15:30加尔各答相同,因此会生成上面的输出。< / p>

正如我在my answer to your other question中解释的那样,Timestamp对象没有任何时区信息。自从unix epoch(1970-01-01T00:00Z“1970年1月1日午夜在UTC”)以来它只有纳秒数。

使用上面的示例,如果您看到value.getCreatedOn().getTime()的值,您会看到它是1505296800000 - 这是自纪元以来的毫秒数(为了获得纳秒精度,有方法{ {1}})。

此毫秒值对应于UTC上午10点,圣保罗上午7点,伦敦上午11点,东京时间下午7点,加尔各答15点30分等。您不会在区域之间转换getNanos(),因为世界各地的millis值都是相同的。

但是,您可以更改的是此值的表示(特定时区中的相应日期/时间)。

在Jackson中,您可以创建自定义序列化程序和反序列化程序(通过扩展Timestampcom.fasterxml.jackson.databind.JsonSerializer),这样您就可以更好地控制格式化和解析日期的方式。

首先,我创建一个将com.fasterxml.jackson.databind.JsonDeserializer格式化为JVM默认时区的序列化程序:

Timestamp

然后我创建一个反序列化器,它读取JVM默认时区中的日期并创建public class TimestampSerializer extends JsonSerializer<Timestamp> { private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); @Override public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { // get the timestmap in the default timezone ZonedDateTime z = value.toInstant().atZone(ZoneId.systemDefault()); String str = fmt.format(z); gen.writeString(str); } }

Timestamp

我还要更改字段以使用这些自定义类:

public class TimestampDeserializer extends JsonDeserializer<Timestamp> {

    private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    @Override
    public Timestamp deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        // parse to a LocalDateTime
        LocalDateTime dt = LocalDateTime.parse(jsonParser.getText(), fmt);
        // the date/time is in the default timezone
        return Timestamp.from(dt.atZone(ZoneId.systemDefault()).toInstant());
    }
}

现在,使用上面的// remove JsonFormat annotation @JsonSerialize(using = TimestampSerializer.class) @JsonDeserialize(using = TimestampDeserializer.class) private Timestamp createdOn; 时(对应Timestamp)。序列化产生:

  

{“createdOn”:“2017-09-13 15:30”}

请注意,现在输出对应于JVM默认时区中的本地时间(2017-09-13T10:00:00Z中的15:30)。

反序列化此JSON时,我返回相同的Asia/Calcutta(对应于UTC的上午10点)。

此代码使用JVM默认时区,但它can be changed without notice, even at runtime,因此最好始终明确指出您正在使用的时区。

API使用IANA timezones names(始终采用Timestamp格式,如Region/CityAsia/Calcutta),因此您可以使用Europe/Berlin创建它们。 避免使用3个字母的缩写(例如ZoneId.of("Asia/Calcutta")IST),因为它们是ambiguous and not standard

您可以致电PST获取可用时区列表(并选择最适合您系统的时区)。

如果您想要将输出更改为与其他时区相对应,只需将ZoneId.getAvailableZoneIds()更改为您想要的区域即可。请记住,必须使用相同的区域来序列化和反序列化,否则您将得到错误的结果。

不是Java 8?

如果你使用Java&lt; = 7,你可以使用ThreeTen Backport,这是Java 8的新日期/时间类的一个很好的后端。

唯一的区别是包名称(在Java 8中为ZoneId.systemDefault()而在ThreeTen Backport中为java.time),但类和方法名称是相同的。< / p>

还有另一个区别:只有在Java 8中,org.threeten.bp类具有方法TimestamptoInstant(),因此您需要使用from()类来制作转化:

org.threeten.bp.DateTimeUtils

答案 1 :(得分:2)

您可以使用timeZone明确指定所需的时区:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm", timezone="Asia/Calcutta")
private Timestamp createdOn;