如何使用objectMapper为java.time.instant设置字符串格式?

2022-08-31 15:06:30

我有一个实体,用于创建的数据字段:java.time.Instant

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Item {
    private String id;
    private String url;
    private Instant createdDate;
}

我正在使用将项目作为JSON保存到Elasticsearch:com.fasterxml.jackson.databind.ObjectMapper

bulkRequestBody.append(objectMapper.writeValueAsString(item));

ObjectMapper将此字段序列化为对象:

"createdDate": {
    "epochSecond": 1502643595,
    "nano": 466000000
}

我正在尝试注释,但它对我不起作用。@JsonFormat(shape = JsonFormat.Shape.STRING)

我的问题是如何将此字段序列化为字符串?2010-05-30 22:15:52


答案 1

一种解决方案是使用 jackson-modules-java8。然后,您可以向对象映射器添加 一个:JavaTimeModule

ObjectMapper objectMapper = new ObjectMapper();

JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);

默认情况下,将 序列化为 epoch 值(单个数字中的秒和纳秒):Instant

{"createdDate":1502713067.720000000}

您可以通过在对象映射器中设置来更改它:

objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

这将产生输出:

{"createdDate":"2017-08-14T12:17:47.720Z"}

上述两种格式都经过反序列化,无需任何其他配置。

要更改序列化格式,只需向字段添加注释:JsonFormat

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Instant createdDate;

您需要设置时区,否则无法正确序列化(它会引发异常)。输出将为:Instant

{"createdDate":"2017-08-14 12:17:47"}

如果您不想(或不能)使用 java8 模块,另一种方法是创建自定义序列化程序和解串器,使用 :java.time.format.DateTimeFormatter

public class MyCustomSerializer extends JsonSerializer<Instant> {

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

    @Override
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        String str = fmt.format(value);

        gen.writeString(str);
    }
}

public class MyCustomDeserializer extends JsonDeserializer<Instant> {

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

    @Override
    public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return Instant.from(fmt.parse(p.getText()));
    }
}

然后,您可以使用这些自定义类对字段进行批注:

@JsonDeserialize(using = MyCustomDeserializer.class)
@JsonSerialize(using = MyCustomSerializer.class)
private Instant createdDate;

输出将为:

{"createdDate":"2017-08-14 12:17:47"}

一个细节是,在序列化字符串中,您将丢弃秒的小数部分(小数点后的所有内容)。因此,在反序列化时,此信息无法恢复(它将设置为零)。

在上面的示例中,原始字符串是 ,但序列化的字符串是(没有秒的分数),因此当反序列化时,结果是(毫秒丢失)。Instant2017-08-14T12:17:47.720Z2017-08-14 12:17:47Instant2017-08-14T12:17:47Z.720


答案 2

对于那些希望解析Java 8时间戳的人。您需要在 POM 中注册最新版本的 ,并注册以下模块:jackson-datatype-jsr310

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

测试此代码

@Test
void testSeliarization() throws IOException {
    String expectedJson = "{\"parseDate\":\"2018-12-04T18:47:38.927Z\"}";
    MyPojo pojo = new MyPojo(ZonedDateTime.parse("2018-12-04T18:47:38.927Z"));

    // serialization
    assertThat(objectMapper.writeValueAsString(pojo)).isEqualTo(expectedJson);

    // deserialization
    assertThat(objectMapper.readValue(expectedJson, MyPojo.class)).isEqualTo(pojo);
}

推荐