gpt4 book ai didi

java - java - 如何在Java中使用Jackson Annotation按原样反序列化时间戳?

转载 作者:行者123 更新时间:2023-12-03 20:44:22 28 4
gpt4 key购买 nike

我的 Java Bean 中有一个字段,如下所示 @JsonFormat是 jackson 注解:

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

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

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

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

最佳答案

我这里做了个测试,把JVM默认时区改成Asia/Calcutta并使用 java.sql.Timestamp 创建一个类 field :

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),请检查底部的“非 Java 8”部分)。首先我创建了一个 Timestamp对应于 2017 年 9 月 13 日上午 10 点,UTC :
TestTimestamp value = new TestTimestamp();
// timestamp corresponds to 2017-09-13T10:00:00 UTC
value.setCreatedOn(Timestamp.from(Instant.parse("2017-09-13T10:00:00Z")));

然后我用Jackson的 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 点 相同15:30 在加尔各答 ,所以产生了上面的输出。

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

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

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

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

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

首先,我创建了一个格式化 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);
}
}

然后我创建一个反序列化器,它读取 JVM 默认时区中的日期并创建 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上面(对应于 2017-09-13T10:00:00Z )。序列化它会产生:

{"createdOn":"2017-09-13 15:30"}



请注意,现在输出对应于 JVM 默认时区中的本地时间( Asia/Calcutta 中的 15:30)。

反序列化此 JSON 时,我得到相同的 Timestamp (对应于 UTC 时间上午 10 点)。

此代码使用 JVM 默认时区,但它 can be changed without notice, even at runtime ,因此最好始终明确说明您使用的是哪一个。

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

您可以调用 ZoneId.getAvailableZoneIds() 获取可用时区列表(并选择最适合您系统的时区)。 .

如果要更改输出以对应另一个时区,只需更改 ZoneId.systemDefault()到你想要的区域。请记住,必须使用同一个区域来进行序列化和反序列化,否则会得到错误的结果。

不是Java 8?

如果您使用的是 Java <= 7,则可以使用 ThreeTen Backport ,Java 8 的新日期/时间类的一个很好的向后移植。

唯一的区别是包名(在 Java 8 中是 java.time 而在 ThreeTen Backport 中是 org.threeten.bp ),但是类和方法 姓名 是相同的。

还有另一个区别:仅在 Java 8 中 Timestamp类有方法 toInstant()from() , 所以你需要使用 org.threeten.bp.DateTimeUtils进行转换的类:
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 = DateTimeUtils.toInstant(value).atZone(ZoneId.systemDefault());
String str = fmt.format(z);

gen.writeString(str);
}
}

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 DateTimeUtils.toSqlTimestamp(dt.atZone(ZoneId.systemDefault()).toInstant());
}
}

关于java - java - 如何在Java中使用Jackson Annotation按原样反序列化时间戳?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46163246/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com