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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45662820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:06:05  来源:igfitidea点击:

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

javajava-8Hymansonjava-timeobjectmapper

提问by Uladzislau Kaminski

I have an entity with java.time.Instantfor 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.Hymanson.databind.ObjectMapperto save item to Elasticsearch as JSON:

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

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

ObjectMapperserializes 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:52string?

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

采纳答案by dassum

One solution is to use Hymanson-modules-java8. Then you can add a JavaTimeModuleto your object mapper:

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

ObjectMapper objectMapper = new ObjectMapper();

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

By default the Instantis 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 JsonFormatannotation 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 Instantcan't be serialized properly (it throws an exception). The output will be:

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

{"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 Instantis 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 Instantis 2017-08-14T12:17:47Z(the .720milliseconds are lost).

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

回答by dassum

You need to add below dependency

您需要添加以下依赖项

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

And then register the modules as below :

然后注册模块如下:

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

回答by UltimaWeapon

For those looking to parse Java 8 timestamps. You need a recent version of Hymanson-datatype-jsr310in your POM and have the following module registered:

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

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

回答by ZZ 5

Here's some Kotlin code of formatting Instant, so it does not contain milliseconds, you can use custom date formatters

这是一些格式化的 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())

回答by Laura Liparulo

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

回答by Valeriy K.

You can use Spring ObjectMapper which already configured with JavaTimeModule. Just inject it from Spring context and don't use new ObjectMapper().

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