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

后端 未结 7 771
失恋的感觉
失恋的感觉 2020-11-29 04:37

I have an entity with java.time.Instant for created data field:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public         


        
相关标签:
7条回答
  • 2020-11-29 05:00

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

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

    {"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(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). 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:

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

    0 讨论(0)
  • 2020-11-29 05:03

    If using Spring, and spring-web is on the classpath, you can create an ObjectMapper using the Jackson2ObjectMapperBuilder. It registers the following commonly used modules within the method 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; see here.

    0 讨论(0)
  • 2020-11-29 05:04

    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();
    
    0 讨论(0)
  • 2020-11-29 05:10

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

    0 讨论(0)
  • 2020-11-29 05:14

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

    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);
    }
    
    0 讨论(0)
  • 2020-11-29 05:15

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

    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())
    
    0 讨论(0)
提交回复
热议问题