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

Ula*_*ski 19 java jackson java-8 java-time objectmapper

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

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Item {
    private String id;
    private String url;
    private Instant createdDate;
}
Run Code Online (Sandbox Code Playgroud)

com.fasterxml.jackson.databind.ObjectMapper用来将项目保存为Elasticsearch作为JSON:

bulkRequestBody.append(objectMapper.writeValueAsString(item));
Run Code Online (Sandbox Code Playgroud)

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

"createdDate": {
    "epochSecond": 1502643595,
    "nano": 466000000
}
Run Code Online (Sandbox Code Playgroud)

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

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

小智 42

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

ObjectMapper objectMapper = new ObjectMapper();

JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
Run Code Online (Sandbox Code Playgroud)

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

{"createdDate":1502713067.720000000}
Run Code Online (Sandbox Code Playgroud)

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

objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Run Code Online (Sandbox Code Playgroud)

这将产生输出:

{"createdDate":"2017-08-14T12:17:47.720Z"}
Run Code Online (Sandbox Code Playgroud)

上述两种格式都是反序列化的,无需任何其他配置.

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

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

您需要设置时区,否则Instant无法正确序列化(抛出异常).输出将是:

{"createdDate":"2017-08-14 12:17:47"}
Run Code Online (Sandbox Code Playgroud)

如果您不想(或不能)使用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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用这些自定义类注释该字段:

@JsonDeserialize(using = MyCustomDeserializer.class)
@JsonSerialize(using = MyCustomSerializer.class)
private Instant createdDate;
Run Code Online (Sandbox Code Playgroud)

输出将是:

{"createdDate":"2017-08-14 12:17:47"}
Run Code Online (Sandbox Code Playgroud)

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

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

  • JsonFormat 注释对我不起作用。但“JavaTimeModule”做到了.. (2认同)

ZZ *_*Z 5 7

这是一些格式化的 Kotlin 代码Instant,因此它不包含毫秒,您可以使用自定义日期格式化程序

ObjectMapper().apply {
        val javaTimeModule = JavaTimeModule()
        javaTimeModule.addSerializer(Instant::class.java, Iso8601WithoutMillisInstantSerializer())
        registerModule(javaTimeModule)
        disable(WRITE_DATES_AS_TIMESTAMPS)
    }

private class Iso8601WithoutMillisInstantSerializer
        : InstantSerializer(InstantSerializer.INSTANCE, false, DateTimeFormatterBuilder().appendInstant(0).toFormatter())
Run Code Online (Sandbox Code Playgroud)


Ult*_*pon 5

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

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Run Code Online (Sandbox Code Playgroud)

测试此代码

@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);
}
Run Code Online (Sandbox Code Playgroud)