简体   繁体   English

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

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

I have an entity with java.time.Instant for created data field:我有一个带有java.time.Instant的实体,用于创建数据字段:

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

I am using com.fasterxml.jackson.databind.ObjectMapper to save item to Elasticsearch as JSON:我正在使用com.fasterxml.jackson.databind.ObjectMapper将项目作为 JSON 保存到 Elasticsearch:

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

ObjectMapper serializes this field as an object: ObjectMapper将此字段序列化为对象:

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

I was trying the annotation @JsonFormat(shape = JsonFormat.Shape.STRING) but it doesn't work for me.我正在尝试注释@JsonFormat(shape = JsonFormat.Shape.STRING)但它对我不起作用。

My question is how I could serialize this field as 2010-05-30 22:15:52 string?我的问题是如何将此字段序列化为2010-05-30 22:15:52字符串?

One solution is to use jackson-modules-java8 .一种解决方案是使用jackson-modules-java8 Then you can add a JavaTimeModule to your object mapper:然后您可以将JavaTimeModule添加到您的对象映射器:

ObjectMapper objectMapper = new ObjectMapper();

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

By default the Instant is serialized as the epoch value (seconds and nanoseconds in a single number):默认情况下, Instant被序列化为纪元值(单个数字中的秒和纳秒):

{"createdDate":1502713067.720000000}

You can change that by setting in the object mapper:您可以通过在对象映射器中设置来更改它:

objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

This will produce the output:这将产生输出:

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

Both formats above are deserialized without any additional configuration.上面的两种格式都是反序列化的,没有任何额外的配置。

To change the serialization format, just add a JsonFormat annotation to the field:要更改序列化格式,只需在字段中添加JsonFormat注释:

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

You need to set the timezone, otherwise the Instant can't be serialized properly (it throws an exception).需要设置时区,否则Instant无法正确序列化(抛出异常)。 The output will be:输出将是:

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

Another alternative, if you don't want to (or can't) use java8 modules, is to create a custom serializer and deserializer, using a java.time.format.DateTimeFormatter :另一种选择,如果你不想(或不能)使用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()));
    }
}

Then you annotate the field with those custom classes:然后使用这些自定义类注释该字段:

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

The output will be:输出将是:

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

One detail is that in the serialized string you're discarding the fraction of second (everything after the decimal point).一个细节是,在序列化字符串中,您丢弃了秒的分数(小数点后的所有内容)。 So, when deserializing, this information can't be recovered (it'll be set to zero).因此,在反序列化时,无法恢复此信息(它将被设置为零)。

In the example above, the original Instant is 2017-08-14T12:17:47.720Z , but the serialized string is 2017-08-14 12:17:47 (without the fraction of seconds), so when deserialized the resulting Instant is 2017-08-14T12:17:47Z (the .720 milliseconds are lost).在上面的例子中,原始Instant2017-08-14T12:17:47.720Z ,但序列化的字符串是2017-08-14 12:17:47 (没有秒的分数),所以当反序列化时,得到的Instant2017-08-14T12:17:47Z (丢失了.720毫秒)。

For those looking to parse Java 8 timestamps.对于那些希望解析 Java 8 时间戳的人。 You need a recent version of jackson-datatype-jsr310 in your POM and have the following module registered:您的 POM 中需要最新版本的jackson-datatype-jsr310并注册以下模块:

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

To test this code要测试此代码

@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);
}

Here's some Kotlin code of formatting Instant , so it does not contain milliseconds, you can use custom date formatters这是格式化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())

You need to add below dependency您需要添加以下依赖项

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

And then register the modules as below :然后注册模块如下:

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

In my case it was enough to register the JavaTimeModule:就我而言,注册 JavaTimeModule 就足够了:

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

  messageObject = objectMapper.writeValueAsString(event);

In the event Object I have a field of type Instant.在事件对象中,我有一个类型为 Instant 的字段。

In the deserialization you also need to register the java time module:在反序列化中还需要注册java时间模块:

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

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

If using Spring, and spring-web is on the classpath, you can create an ObjectMapper using the Jackson2ObjectMapperBuilder .如果使用 Spring,并且spring-web在类路径上,则可以使用Jackson2ObjectMapperBuilder创建一个ObjectMapper It registers the following commonly used modules within the method registerWellKnownModulesIfAvailable .它在 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

Some of these modules have been merged into Jackson 3;其中一些模块已合并到 Jackson 3 中; see here .看到这里

You can use Spring ObjectMapper which already configured with JavaTimeModule.您可以使用已经配置了 JavaTimeModule 的 Spring ObjectMapper。 Just inject it from Spring context and don't use new ObjectMapper() .只需从 Spring 上下文中注入它,不要使用new ObjectMapper()

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

相关问题 如何在 java.time.Instant 中获取和设置指定时间? - How to get and set specified time in java.time.Instant? 如何在 Spring Webflux 中将 java.time.Instant 序列化为 ISO 字符串 - How to serialize java.time.Instant as ISO string in Spring Webflux Hibernate-在PostgreSQL中使用java.time.Instant - Hibernate - Using java.time.Instant with PostgreSQL java.time.Instant 是如何计算纳秒的? - How are nanoseconds calculated in java.time.Instant? “com.fasterxml.jackson.databind.JsonMappingException:预期类型为浮点数、整数或字符串。” 使用 ObjectMapper 转换 java.time.Instant - "com.fasterxml.jackson.databind.JsonMappingException: Expected type float, integer, or string." converting the java.time.Instant with ObjectMapper 使用 Jackson ObjectMapper 在 java.time.Instant 和 java.util.Date 之间进行转换 - Use Jackson ObjectMapper to convert between java.time.Instant and java.util.Date 如何在 Swagger 2.x 中使用 java.time.Instant? - How to use java.time.Instant with Swagger 2.x? 如何在 Jackson2JsonRedisSerializer 中反序列化 java.time.Instant - How to deserialize java.time.Instant in Jackson2JsonRedisSerializer java.time.Instant(1.8)是线程安全的吗? - java.time.Instant (1.8) is thread safe? 逗号无法通过java.time.Instant解析 - Comma fails parsing by java.time.Instant
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM