code

잭슨이 Spring Boot에서 ZonedDateTime을 잘못 시리얼화함

starcafe 2023. 2. 12. 18:04
반응형

잭슨이 Spring Boot에서 ZonedDateTime을 잘못 시리얼화함

Spring Boot과 Jetty의 심플한 어플이 있습니다.Java 8을 탑재한 오브젝트를 반환하는 심플한 컨트롤러가 있다.ZonedDateTime:

public class Device {
  // ...
  private ZonedDateTime lastUpdated;

  public Device(String id, ZonedDateTime lastUpdated, int course, double latitude, double longitude) {
    // ...
    this.lastUpdated = lastUpdated;
    // ...
  }

  public ZonedDateTime getLastUpdated() {
    return lastUpdated;
  }
}

인마이RestController다음과 같은 것이 있습니다.

@RequestMapping("/devices/")
public @ResponseBody List<Device> index() {
  List<Device> devices = new ArrayList<>();
  devices.add(new Device("321421521", ZonedDateTime.now(), 0, 39.89011333, 24.438176666));

  return devices;
}

난 기대했어ZonedDateTimeISO 포맷에 따라 포맷됩니다만, 대신 다음과 같은 클래스의 JSON 덤프 전체를 가져옵니다.

"lastUpdated":{"offset":{"totalSeconds":7200,"id":"+02:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"zone":{"id":"Europe/Berlin","rules":{"fixedOffset":false,"transitionRules":[{"month":"MARCH","timeDefinition":"UTC","standardOffset":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"offsetBefore":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"offsetAfter":{"totalSeconds":7200,"id":"+02:00", ...

저는 그냥...spring-boot-starter-web응용 프로그램, 사용spring-boot-starter-jetty제외하다spring-boot-starter-tomcat.

스프링 부츠에서 잭슨은 왜 이렇게 행동하지?

** 업데이트 **

이 문제를 해결하는 방법에 대한 자세한 가이드를 찾으시는 분들을 위해 다음 질문을 한 후 이 가이드를 찾았습니다.http://lewandowski.io/2016/02/formatting-java-time-with-spring-boot-using-json/

라이브러리 jackson-datatype-jsr310이 있습니다.먹어봐.

이 라이브러리에서는 새로운 datetime API에 대해 설명하며 의 시리얼라이저를 포함합니다.ZonedDateTime너무.

추가만 하면 됩니다.JavaTimeModule:

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

갱신하다

datetime을 문자열로 변환하려면 기능을 비활성화해야 합니다.다음 중 하나를 덮어쓰면 쉽게 할 수 있습니다.ObjectMapperbean 또는 응용 프로그램 속성 사용:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false

Spring Boot의 자동 구성 기능에 의존하지 않는 경우,spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false컨피규레이션파일에 속성을 저장합니다.또는 어떤 이유로 작성하든 상관없습니다.ObjectMapper수동으로 인스턴스화 합니다.다음과 같이 이 기능을 프로그램적으로 디세블로 할 수 있습니다.

ObjectMapper m = new ObjectMapper();
m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

이것은 잭슨을 위한 것이다.2.8.7

답변은 이미 위에서 언급되었지만 정보가 빠진 것 같습니다.Java 8 타임스탬프를 (ZonedDateTime뿐만 아니라) 다양한 형식으로 해석하려는 사용자용.의 최신 버전이 필요합니다.jackson-datatype-jsr310다음 모듈을 등록합니다.

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

이 코드를 테스트하려면

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

Spring 또는 drop wizard에서 오브젝트 맵퍼를 글로벌하게 설정할 수 있습니다.커스텀(디)시리얼라이저를 등록하지 않고 필드의 주석으로 이 작업을 수행할 수 있는 적절한 방법을 아직 찾지 못했습니다.

잭슨을 위해서2.10그 이상은

부모 pom.xml

<!-- https://github.com/FasterXML/jackson-bom -->
<dependencyManagement>
  <dependency>
    <groupId>com.fasterxml.jackson</groupId>
    <artifactId>jackson-bom</artifactId>
    <version>2.10.3</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencyManagement>

모듈 pom.xml

<!-- https://github.com/FasterXML/jackson-modules-java8 -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

JsonMapper 생성(아마도 사용자 이름에서)@Configuration학급

@Bean
public JsonMapper jsonMapper() {
    return JsonMapper.builder()
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .addModule(new JavaTimeModule())
        .build();
}

추가 정보:

설정spring.jackson.serialization.write-dates-as-timestamps=false에서application.yml우리 프로젝트에 도움이 되지 않는다.아마도 잭슨과 함께 작업하는 추가 라이브러리가 있을 것입니다.Swagger / OpenAPI / OpenAPI Generator.

해서 도움이 된 것은 이 '이러다', '이러다'를 입니다.@EventListener★★★★★★★★★★★★★★★★의 경우RequestMappingHandlerAdapter@SpringBootApplication를 누릅니다

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

@SpringBootApplication
public class SpringBootInitializer {

    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    public static void main(String[] args) {
        SpringApplication.run(SpringBootInitializer.class, args);
    }

    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        // see https://github.com/FasterXML/jackson-modules-java8/issues/11#issuecomment-913199874
        // spring.jackson.serialization.write-dates-as-timestamps=false setting does not work in our configuration (probably because of Swagger / OpenAPI / OpenAPI Generator libraries used)

        handlerAdapter
            .getMessageConverters()
            .forEach(c -> {
                if (c instanceof MappingJackson2HttpMessageConverter jsonMessageConverter) {
                    ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
                    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                }
            });
    }
}

이 솔루션은 https://github.com/FasterXML/jackson-modules-java8/issues/11#issuecomment-913199874에서 찾을 수 있습니다.

언급URL : https://stackoverflow.com/questions/39086472/jackson-serializes-a-zoneddatetime-wrongly-in-spring-boot

반응형