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

如何使用objectMapper设置java.time.Instant的字符串格式?,java,java-8,jackson,java-time,objectmapper,Java,Java 8,Jackson,Java Time,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

对于创建的数据字段,我有一个带有
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
string?

您需要在下面添加依赖项

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.5</version>
</dependency>
一个解决方案是使用。然后,您可以向对象映射器添加
JavaTimeModule

ObjectMapper objectMapper = new ObjectMapper();

JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
默认情况下,
瞬间
被序列化为历元值(单个数字中的秒和纳秒):

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

ObjectMapper objectMapper = new ObjectMapper();

JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
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;
您需要设置时区,否则,
即时
无法正确序列化(它会引发异常)。输出将是:

{"createdDate":"2017-08-14 12:17:47"}
{"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()));
    }
}
输出将是:

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

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


在上面的示例中,原始的
即时
2017-08-14T12:17:47.720Z
,但序列化的字符串是
2017-08-14 12:17:47
(不含秒数),因此反序列化时,结果的
即时
2017-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);
}

这里有一些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())

在我的例子中,注册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);

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

如果使用Spring,并且
SpringWeb
位于类路径上,则可以使用
Jackson2ObjectMapperBuilder
创建
ObjectMapper
。它在方法
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中;请参阅。

@jbnize谢谢您的回答,我添加了
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false)
objectMapper.findAndRegisterModules()
它对我不起作用。您是否已将模块jar文件添加到您的类路径中?当然,
编译组:'com.fasterxml.jackson.module',名称:'jackson-modules-java8',版本:'2.9.0',分机:'pom'JavaTimeModule
确实..不适用于我的Java8模块和
WRITE_DATES_AS_TIMESTAMPS
false,我得到错误
no String-argument构造函数/工厂方法从字符串值('2020-08-13T10:46:16.860Z')
对于Jackson版本>
2.10
,请看这一点。这里最重要的是“
newObjectMapper().registerModule(newJavaTimeModule())应在序列化期间处于活动状态。否则,Objectmapper将使用
epochSecond
nano
将即时数据类型转换为嵌套对象。“仅当
spring web
位于类路径上时才适用。对于Jackson版本>
2.10
,请参见此。