繁体   English   中英

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

[英]How to set format of string for java.time.Instant using objectMapper?

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

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

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

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

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

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

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

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

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

ObjectMapper objectMapper = new ObjectMapper();

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

默认情况下, 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.720Z ,但序列化的字符串是2017-08-14 12:17:47 (没有秒的分数),所以当反序列化时,得到的Instant2017-08-14T12:17:47Z (丢失了.720毫秒)。

对于那些希望解析 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);
}

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

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())

您需要添加以下依赖项

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.5</version>
</dependency>

然后注册模块如下:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();

就我而言,注册 JavaTimeModule 就足够了:

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

  messageObject = objectMapper.writeValueAsString(event);

在事件对象中,我有一个类型为 Instant 的字段。

在反序列化中还需要注册java时间模块:

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

Event event = objectMapper.readValue(record.value(), Event.class);

如果使用 Spring,并且spring-web在类路径上,则可以使用Jackson2ObjectMapperBuilder创建一个ObjectMapper 它在 registerWellKnownModulesIfAvailable 方法中registerWellKnownModulesIfAvailable了以下常用模块。

com.fasterxml.jackson.datatype.jdk8.Jdk8Module
com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
com.fasterxml.jackson.datatype.joda.JodaModule
com.fasterxml.jackson.module.kotlin.KotlinModule

其中一些模块已合并到 Jackson 3 中; 看到这里

您可以使用已经配置了 JavaTimeModule 的 Spring ObjectMapper。 只需从 Spring 上下文中注入它,不要使用new ObjectMapper()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM