Spring Boot Jackson日期格式

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

Spring Boot Jackson date format

问题

I have a problem with Jackson in SpringBoot. My controller returns dates in format yyyy-MM-dd'T'HH:mm:ss'Z', but I need yyyy-MM-dd'T'HH:mm:ss.SSS'Z' (for mwl-calendar in angular which uses date-fns library).

Controller:

@GetMapping(value = "/slots", produces = MediaType.APPLICATION_JSON_VALUE)
public Set<SlotResponse> timeSlots() {
    return slotService.getSlots();
}

SlotResponse:

@Data
public class SlotResponse {
    private Instant start;
    private Instant end;
}

Additional dependency:

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

I tried:

  1. to use @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") annotation
  2. to use spring.jackson.date-format=yyyyMMdd'T'HH:mm:ss.SSSZ configuration
  3. to create ObjectMapper manually:
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
    return mapper;
}
  1. to enable options SerializationFeature.WRITE_DATES_WITH_ZONE_ID and SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE

But all of this didn't take effect. Every time I see this result:

[{"start":"2023-05-11T16:00:00Z","end":"2023-05-11T17:00:00Z"}]

I use Java 17, SpringBoot 2.7.0, Jackson 2.13.3

What is wrong?

英文:

I have a problem with Jackson in SpringBoot. My controller returns dates in format yyyy-MM-dd&#39;T&#39;HH:mm:ss&#39;Z&#39;, but I need yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39; (for mwl-calendar in angular which uses date-fns library).

Controller:

@GetMapping(value = &quot;/slots&quot;, produces = MediaType.APPLICATION_JSON_VALUE)
public Set&lt;SlotResponse&gt; timeSlots() {
    return slotService.getSlots();
}

SlotResponse:

@Data
public class SlotResponse {
    private Instant start;
    private Instant end;
}

Additional dependency:

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

I tried:

  1. to use @JsonFormat(pattern=&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39;&quot;) annotation
  2. to use spring.jackson.date-format=yyyyMMddTHH:mm:ss.SSSZ configuration
  3. to create ObjectMapper manually:
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    mapper.setDateFormat(new SimpleDateFormat(&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39;&quot;));
    return mapper;
}
  1. to enable options SerializationFeature.WRITE_DATES_WITH_ZONE_ID and SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE

But all of this didn't take effect. Everytime I see this result:

[{&quot;start&quot;:&quot;2023-05-11T16:00:00Z&quot;,&quot;end&quot;:&quot;2023-05-11T17:00:00Z&quot;}]

I use Java 17, SpringBoot 2.7.0, Jackson 2.13.3

What is wrong?

答案1

得分: 3

你可以为Instant类型创建自定义的JSON序列化器:

```java
public static class InstantSerializer extends StdSerializer<Instant> {

    public InstantSerializer() {
        super(Instant.class);
    }

    @Override
    public void serialize(
        @Nullable final Instant value,
        @NonNull final JsonGenerator jsonGenerator,
        @NonNull final SerializerProvider serializerProvider
    ) throws IOException {
        if (value != null) {
            final DateTimeFormatter formatter = DateTimeFormatter
                .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
                .withZone(ZoneId.systemDefault());
            final String text = formatter.format(value);
            jsonGenerator.writeString(text);
        }
    }

}

然后在你想要按照这个格式处理的字段上使用它:

@Data
public class SlotResponse {

    @JsonSerialize(using = InstantSerializer.class) // Add this
    
    private Instant start;
    @JsonSerialize(using = InstantSerializer.class) // Add this
    private Instant end;
}

如果你想让这个适用于整个系统,你可以将这个序列化器添加到Jackson中:

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    ...
    final SimpleModule module = new SimpleModule();
    module.addSerializer(new InstantSerializer());
    mapper.registerModule(module);
    ...
    return mapper;
}

<details>
<summary>英文:</summary>

You can create custom json serializer for Instant type:
public static class InstantSerializer extends StdSerializer&lt;Instant&gt; {

    public InstantSerializer() {
        super(Instant.class);
    }

    @Override
    public void serialize(
        @Nullable final Instant value,
        @NonNull final JsonGenerator jsonGenerator,
        @NonNull final SerializerProvider serializerProvider
    ) throws IOException {
        if (value != null) {
            final DateTimeFormatter formatter = DateTimeFormatter
                .ofPattern(&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39;&quot;)
                .withZone(ZoneId.systemDefault());
            final String text = formatter.format(value);
            jsonGenerator.writeString(text);
        }
    }

}
Then use it on the field that you want to follow the format:
@Data
public class SlotResponse {

    @JsonSerialize(using = InstantSerializer.class) // Add this
    
    private Instant start;
    @JsonSerialize(using = InstantSerializer.class) // Add this
    private Instant end;
}
In case you want to make this apply for your whole system, you can add the serializer to jackson:
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    ...
    final SimpleModule module = new SimpleModule();
    module.addSerializer(new InstantSerializer());
    mapper.registerModule(module);
    ...
    return mapper;
}

</details>



# 答案2
**得分**: 1

I apologize, but the text you provided contains a mix of code, formatting, and explanations. It's challenging to provide a meaningful translation without context. If you have specific parts that need translation or if you'd like a summary of the content, please specify, and I'll be happy to assist.

<details>
<summary>英文:</summary>

### Sorry, cannot reproduce

With (*only*)
- spring-boot-starter-web:2.7.0
- lombok
- (dev tools, spring-boot-starter-test)

, (shortened) &quot;dto&quot;:

package com.example.soq76225352;

import java.time.Instant;
import lombok.Data;

@Data
public class SomeResponse {
private Instant start = Instant.now();
}


and: 
```lang-java
@RestController
class DemoController {

  @GetMapping(value = &quot;/slots&quot;)
  public SomeResponse timeSlots() {
    return new SomeResponse();
  }
}

(no other settings, properties, only defaults..)
, we get:

{&quot;start&quot;:&quot;2023-05-11T09:08:23.569050400Z&quot;}

which looks very like the desired format. (??)


As soon I add:

@JsonFormat(pattern = &quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39;&quot;)

, I get:

2023-05-11 11:03:49.487  WARN 3732 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver :
   Resolved [org.springframework.http.converter.HttpMessageNotWritableException:
     Could not write JSON: Unsupported field: YearOfEra; nested exception is com.fasterxml.jackson.databind.JsonMappingException:
       Unsupported field: YearOfEra (through reference chain: 
         com.example.soq76225352.SomeResponse[&quot;start&quot;])
   ]

(-> /error -> 404) !?


Going deeper

  • The format &#39;Z&#39; refers to a "literal Z"
  • Whereas Z refers to the "zone offset" (in form +HHMM, see DateTimeFormatterBuilder + source documentation)
  • On the other hand: X is the "zone offset" (as described by Z) or (literal) &#39;Z&#39; in case of "zero offset"/UTC! DateTimeFormatter Spring Boot Jackson日期格式

To make it work:

  • in all cases (that's strange for the literal case!), we have to:

    • or @JsonFormat(timezone = &quot;...&quot; //,...
    • or spring.jackson.time-zone

    ... (explicitly) set the "config time zone", see https://stackoverflow.com/q/7556851/592355

    • additionally we need to ensure (it is by default): spring.jackson.serialization.WRITE_DATES_WITH_CONTEXT_TIME_ZONE=true
      , see source code(your version)

Given (e.g.)

spring.jackson.time-zone=UTC

When/Then

  • When format is &#39;Z&#39;, then we get &quot;...Z&quot;
  • When format is Z, then we get &quot;...+0000&quot;
  • When format is X, then we get (also) &quot;...Z&quot;

Relevant, but old:

Relevant, but not deep/exact (milliseconds) enough:

huangapple
  • 本文由 发表于 2023年5月11日 16:01:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76225352.html
匿名

发表评论

匿名网友

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

确定