将Instant转换为ISO8601日期格式在Spring Boot中不起作用。

huangapple go评论104阅读模式
英文:

Convert Instant to ISO8601 date format not working in Spring Boot

问题

我正在使用Spring Boot 2.3.4.RELEASE,在尝试将包含Instant属性的DTO转换为JSON格式时,jackson ObjectMapper仍然将其转换为时间戳格式,即使已经关闭了write-dates-as-timestamps选项。

pom.xml

    <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.3.4.RELEASE</version>
         <relativePath/> <!-- lookup parent from repository -->
    </parent> 
    
    ....
    
    <dependency>
         <groupId>com.fasterxml.jackson.datatype</groupId>
         <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

application.properties

    spring.jackson.serialization.write-dates-as-timestamps = false

DTO:

    @Data
    public class MyDTO {

        Long id;

        @NotNull
        String number;

        @NotNull
        Integer year;
    
        @NotNull
        VesselContractStatusEnum status;
    
        Instant statusDate;
    }
  
Rest Controller response:

    {
        "id": 1,
        "number": "202000",
        "year": 2020,
        "status": "Open",
        "statusDate": 1602298800
     }

根据在StackOverflow中找到的建议,我尝试了以下方法来覆盖ObjectMapper,但没有成功。

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {

        ObjectMapper objectMapper = builder.build();

        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        objectMapper.registerModule(javaTimeModule);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));

        return objectMapper;
    }
英文:

I'm using spring boot 2.3.4.RELEASE and when trying to convert a DTO containing an Instant attribute to JSON format, the jackson ObjectMapper keeps converting it to timestamp format, even with write-dates-as-timestamps option turned off.

pom.xml

&lt;parent&gt;
     &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
     &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
     &lt;version&gt;2.3.4.RELEASE&lt;/version&gt;
     &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
&lt;/parent&gt; 

....

&lt;dependency&gt;
     &lt;groupId&gt;com.fasterxml.jackson.datatype&lt;/groupId&gt;
     &lt;artifactId&gt;jackson-datatype-jsr310&lt;/artifactId&gt;
&lt;/dependency&gt;

application.properties

spring.jackson.serialization.write-dates-as-timestamps = false

DTO:

@Data
public class MyDTO {

    Long id;

    @NotNull
    String number;

    @NotNull
    Integer year;

    @NotNull
    VesselContractStatusEnum status;

    Instant statusDate;
}

Rest Controller response:

{
    &quot;id&quot;: 1,
    &quot;number&quot;: &quot;202000&quot;,
    &quot;year&quot;: 2020,
    &quot;status&quot;: &quot;Open&quot;,
    &quot;statusDate&quot;: 1602298800
 }

Following recommendations found here in StackOverflow, I tried to override the ObjectMapper using the follwoing approach, but it didn't work.

@Bean
@Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {

    ObjectMapper objectMapper = builder.build();

    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

    JavaTimeModule javaTimeModule = new JavaTimeModule();
    objectMapper.registerModule(javaTimeModule);
    objectMapper.setDateFormat(new SimpleDateFormat(&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39;&quot;));

    return objectMapper;
}

答案1

得分: 1

Instant.toString()返回ISO8601格式的字符串(尽其所值)。也许为Json字段提供一个getter会有帮助?

英文:

Instant.toString() returns the ISO8601 formatted string (for what its worth). Perhaps providing a getter for the Json field would help?

答案2

得分: 1

你需要按照以下方式编写自定义的序列化器和反序列化器类:

序列化器类:

public class InstantSerializer extends JsonSerializer<Instant> {

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);

    @Override
    public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        String str = dateTimeFormatter.format(instant);

        jsonGenerator.writeString(str);
    }
}

反序列化器类:

public class InstantDeserializer extends JsonDeserializer<Instant> {

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);

    @Override
    public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return Instant.from(dateTimeFormatter.parse(jsonParser.getText()));
    }
}

然后在你的 DTO 类中的 Instant 变量上方添加以下注解:

@JsonDeserialize(using = InstantDeserializer.class)
@JsonSerialize(using = InstantSerializer.class)
Instant statusDate;
英文:

You have to write a Custom Serializer and De Serializer classes as below:

Serializer Class:

public class InstantSerializer extends JsonSerializer&lt;Instant&gt; {

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss&quot;).withZone(ZoneOffset.UTC);

    @Override
    public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        String str = dateTimeFormatter.format(instant);

        jsonGenerator.writeString(str);
    }
}

Deserializer Class:

public class InstantDeserializer extends JsonDeserializer&lt;Instant&gt; {

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss&quot;).withZone(ZoneOffset.UTC);

    @Override
    public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return Instant.from(dateTimeFormatter.parse(jsonParser.getText()));
    }
}

Then add the below annotations on top of the Instant variable in your DTO class as below:

@JsonDeserialize(using = InstantDeserializer.class)
@JsonSerialize(using = InstantSerializer.class)
Instant statusDate;

答案3

得分: 1

你是否在任何地方使用了注解

@EnableWebMvc

经过尝试了各种方法仍然没有成功,我通过移除我在主类上(使用 @SpringBootApplication 注解)的 @EnableWebMvc 注解使其工作起来。

我认为 @EnableWebMvc 接管了所有的 MVC 配置控制。
如果你真的需要 @EnableWebMvc,那么你必须手动注册序列化器/反序列化器。
例如,通过将类似下面这样的内容添加到主类上(使用 @SpringBootApplication 注解):

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
  {
    SimpleModule m = new SimpleModule();
    m.addSerializer(Instant.class, new MyCustomJsonSerializer());
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().modules(m);
    converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
  }
英文:

Do you have annotation

@EnableWebMvc

anywhere?

After having tried everything without success, I got it to work by removing the @EnableWebMvc annotation I had on the main class (annotated with @SpringBootApplication).

I think @EnableWebMvc takes over all MVC configuration control.
If you really need @EnableWebMvc, then you have to register the serializer / deserializer by hand.
Eg. by adding something like this to the main class (annotated with @SpringBootApplication):

  @Override
  public void configureMessageConverters(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)
  {
    SimpleModule m = new SimpleModule();
    m.addSerializer(Instant.class, new MyCustomJsonSerializer());
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().modules(m);
    converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
  }

huangapple
  • 本文由 发表于 2020年10月27日 21:04:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/64555089.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定