Jackson JSON 매퍼로 java 8 java.time 직렬화 / 직렬화 해제


221

Java 8 LocalDateTime에서 Jackson JSON 매퍼를 어떻게 사용합니까?

org.codehaus.jackson.map.JsonMappingException : JSON 문자열에서 [단순 유형, 클래스 java.time.LocalDateTime] 유형의 값을 인스턴스화 할 수 없습니다. 단일 문자열 생성자 / 공장 메소드 없음 (참조 체인을 통해 : MyDTO [ "field1"]-> SubDTO [ "date"])


4
LocalDateTime을 JSon에 매핑 하시겠습니까? 내가 아는 한 JSon은 날짜 형식을 갖지 않지만 JavaScript는 ISO-8601을 사용합니다. 문제는 LocalDateTime에 시간대가 없다는 것입니다. 따라서 JSON을 매체로 사용하여 날짜 / 시간 정보를 보내면 클라이언트가 시간대 부족을 기본 UTC (또는 자체 UTC)로 해석하면 문제가 발생할 수 있습니다 시간대). 그것이 당신이하고 싶은 일이라면 물론 괜찮습니다. 그러나 ZonedDateTime 사용을 고려했는지 확인하십시오
arcuri82

답변:


276

여기서는 사용자 정의 시리얼 라이저 / 디시리얼라이저를 사용할 필요가 없습니다. jackson-modules-java8의 datetime 모듈을 사용하십시오 .

Jackson이 Java 8 날짜 및 시간 API 데이터 유형 (JSR-310)을 인식하도록하는 데이터 유형 모듈.

이 모듈은 몇 가지 클래스에 대한 지원을 추가합니다.

  • 지속
  • 즉시
  • LocalDateTime
  • 현지 날짜
  • 현지 시각
  • 월 일
  • 오프셋 날짜
  • 오프셋 시간
  • 기간
  • YearMonth
  • ZonedDateTime
  • ZoneId
  • ZoneOffset

59
어 그래! 이 생각 나는 하나가 매퍼 개체에 이러한 정의 중 하나를 수행해야한다는 것을 인식하지 않았기 때문에 작동하지 않는 : registerModule(new JSR310Module())findAndRegisterModules(). github.com/FasterXML/jackson-datatype-jsr310을 참조하십시오. Spring 프레임 워크를 사용하는 경우 매퍼 객체를 사용자 정의하는 방법 은 다음 과 같습니다. stackoverflow.com/questions/7854030/…
Alexander Taylor

10
내가 시도한 가장 간단한 방법으로는 작동하지 않습니다.OffsetDateTime @Test public void testJacksonOffsetDateTimeDeserializer() throws IOException { ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); String json = "\"2015-10-20T11:00:00-8:30\""; mapper.readValue(json, OffsetDateTime.class); }
Abhijit Sarkar

9
최근 Spring 프레임 워크를 사용하는 경우 더 이상 사용자 정의 할 필요가 없습니다. 이와 같은 잘 알려진 Jackson 모듈은 클래스 경로에서 감지되면 자동으로 등록됩니다. spring.io/blog/2014/12/02/…
vorburger

36
JSR310Module은 이미 구식입니다. 대신 JavaTimeModule을 사용하십시오.
Jacob Eckel

17
@MattBall jackson-datatype-jsr310을 사용하는 것 외에도 객체 매퍼에 JavaTimeModule을 등록해야합니다 objectMapper.registerModule(new JavaTimeModule());. 직렬화와 역 직렬화 모두에서.
GrandAdmiral

76

업데이트 : 역사적인 이유로이 답변을 남기지 만 권장하지 않습니다. 위의 허용 된 답변을 참조하십시오.

Jackson에게 사용자 정의 직렬화 클래스를 사용하여 맵핑하도록 지시하십시오.

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime ignoreUntil;

사용자 정의 클래스를 제공하십시오.

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
        arg1.writeString(arg0.toString());
    }
}

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {
        return LocalDateTime.parse(arg0.getText());
    }
}

임의의 사실 : 클래스 위에 중첩하고 정적으로 만들지 않으면 오류 메시지가 이상합니다. org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported


이 글을 쓰는 시점에서 다른 답변에 비해 여기서 유일한 장점은 FasterXML에 의존하지 않는다는 것입니다.
Alexander Taylor

4
FasterXML Jackson입니다. fasterxml.com github.com/fasterxml/jackson
Matt Ball

4
오 알 겠어요 내가 작업중 인 프로젝트에 너무 많은 고대 퐁 항목이 있습니다. 더 이상 org.codehaus는 없으며 이제 모든 com.fasterxml입니다. 감사!
Alexander Taylor

2
또한 포맷터 ISO_DATE_TIME을 전달해야했기 때문에 내장 기능을 사용할 수 없었습니다. JSR310Module을 사용할 때 그렇게 할 수 있는지 확실하지 않습니다.
isaac.hazan

봄 나는 이것을 사용하는 것은 모두 빈을 만들어야했습니다 LocalDateTimeSerializerLocalDateTimeDeserializer
BenR

53

fasterxml의 ObjectMapper 클래스를 사용하는 경우 기본적으로 ObjectMapper는 LocalDateTime 클래스를 이해하지 못하므로 gradle / maven에 다른 종속성을 추가해야합니다.

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3'

이제이 라이브러리에서 제공하는 데이터 유형 지원을 objectmapper 객체에 등록해야합니다.이 작업은 다음과 같이 수행 할 수 있습니다.

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

이제 jsonString에서 다음과 같이 java.LocalDateTime 필드를 쉽게 넣을 수 있습니다.

{
    "user_id": 1,
    "score": 9,
    "date_time": "2016-05-28T17:39:44.937"
}

이 모든 작업을 수행하면 Json 파일에서 Java 객체로의 변환이 정상적으로 작동합니다. 다음과 같이 파일을 읽을 수 있습니다.

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
            });

4
findAndRegisterModules()구성 할 때 나 누락 된 중요한 부분이었다ObjectMapper
Xaero Degreaz

2
인스턴트는 어떻습니까?
powder366

1
나는 또한이 줄을 추가했다 : objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Janet

이것은 완전한 코드입니다. ObjectMapper objectMapper = new ObjectMapper (); objectMapper.findAndRegisterModules (); objectMapper.configure (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
khush

42

이 maven 종속성은 문제를 해결합니다.

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

내가 어려움을 겪은 한 가지는 직렬화 해제 중에 ZonedDateTime 시간대가 GMT로 변경된다는 것입니다. 기본적으로 jackson은이를 컨텍스트에서 하나로 대체합니다. 영역을 유지하려면이 '기능'을 비활성화해야합니다.

Jackson2ObjectMapperBuilder.json()
    .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)

5
DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE 힌트에 감사드립니다.
Andrei Urvantcev

5
비활성화뿐만 아니라 모든 것이 작동하기 시작 DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE하도록 비활성화 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS해야했습니다.
Matt Watson

16

스프링 부트 를 사용하는 동안 비슷한 문제가 발생했습니다 . Spring boot 1.5.1.RELEASE를 사용하면 종속성을 추가하기 만하면됩니다.

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

7

Jersey를 사용하는 경우 다른 사람들이 제안한대로 Maven 종속성 (jackson-datatype-jsr310)을 추가하고 다음과 같이 객체 매퍼 인스턴스를 등록해야합니다.

@Provider
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {

  final ObjectMapper defaultObjectMapper;

  public JacksonObjectMapper() {
    defaultObjectMapper = createDefaultMapper();
  }

  @Override
  public ObjectMapper getContext(Class<?> type) {
    return defaultObjectMapper;
  }

  private static ObjectMapper createDefaultMapper() {
    final ObjectMapper mapper = new ObjectMapper();    
    mapper.registerModule(new JavaTimeModule());
    return mapper;
  }
}

리소스에 Jackson을 등록 할 때 다음과 같이이 매퍼를 추가해야합니다.

final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
  .register(JacksonObjectMapper.class)
  .register(JacksonJaxbJsonProvider.class);

6

jackson-modules-java8어떤 이유로 든 사용할 수없는 경우 and & 를 long사용하여 인스턴트 필드를 직렬화 해제 할 수 있습니다 .@JsonIgnore@JsonGetter@JsonSetter

public class MyBean {

    private Instant time = Instant.now();

    @JsonIgnore
    public Instant getTime() {
        return this.time;
    }

    public void setTime(Instant time) {
        this.time = time;
    }

    @JsonGetter
    private long getEpochTime() {
        return this.time.toEpochMilli();
    }

    @JsonSetter
    private void setEpochTime(long time) {
        this.time = Instant.ofEpochMilli(time);
    }
}

예:

@Test
public void testJsonTime() throws Exception {
    String json = new ObjectMapper().writeValueAsString(new MyBean());
    System.out.println(json);
    MyBean myBean = new ObjectMapper().readValue(json, MyBean.class);
    System.out.println(myBean.getTime());
}

수확량

{"epochTime":1506432517242}
2017-09-26T13:28:37.242Z

이것은 매우 깨끗한 방법이며 클래스 사용자는 원하는 방식으로 사용할 수 있습니다.
Ashhar Hasan 2019

3

이 시간 형식을 사용합니다 : "{birthDate": "2018-05-24T13:56:13Z}"json에서 java.time.Instant로 직렬화 해제하려면 (스크린 샷 참조)

여기에 이미지 설명을 입력하십시오


3

이것은이 문제를 디버깅하기 위해 해킹 한 단위 테스트에서 사용하는 방법에 대한 예입니다. 주요 성분은

  • mapper.registerModule(new JavaTimeModule());
  • 의 maven 의존성 <artifactId>jackson-datatype-jsr310</artifactId>

암호:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;

class Mumu implements Serializable {
    private Instant from;
    private String text;

    Mumu(Instant from, String text) {
        this.from = from;
        this.text = text;
    }

    public Mumu() {
    }

    public Instant getFrom() {
        return from;
    }

    public String getText() {
        return text;
    }

    @Override
    public String toString() {
        return "Mumu{" +
                "from=" + from +
                ", text='" + text + '\'' +
                '}';
    }
}
public class Scratch {


    @Test
    public void JacksonInstant() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());

        Mumu before = new Mumu(Instant.now(), "before");
        String jsonInString = mapper.writeValueAsString(before);


        System.out.println("-- BEFORE --");
        System.out.println(before);
        System.out.println(jsonInString);

        Mumu after = mapper.readValue(jsonInString, Mumu.class);
        System.out.println("-- AFTER --");
        System.out.println(after);

        Assert.assertEquals(after.toString(), before.toString());
    }

}

2

Spring 부트를 사용하고 OffsetDateTime 에서이 문제가 발생하면 @greperror (answer. 5 월 28 '16 at 13:04에 의해 답변 됨)에 의해 registerModules를 사용해야하지만 한 가지 차이점이 있습니다. 스프링 부트에 이미 있다고 가정하므로 언급 된 종속성을 추가 할 필요가 없습니다. 나는 스프링 부트 에서이 문제를 겪고 있었고이 의존성을 추가하지 않고 나를 위해 일했다.


1

application.ymljava8의 Date API 인 Instant time을 해결하기 위해 파일 에서 이것을 설정할 수 있습니다 .

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

1

Spring Boot 2.x 를 사용하는 사람들

위의 작업을 수행 할 필요가 없습니다. Java 8 LocalDateTime은 기본적으로 직렬화 / 역 직렬화됩니다. 1.x에서 위의 모든 작업을 수행해야했지만 Boot 2.x에서는 완벽하게 작동합니다.

스프링 부트의 JSON Java 8 LocalDateTime 형식 도 참조하십시오.


1

SpringBoot여기에 사용하는 동안 문제가있는 사람 은 새로운 종속성을 추가하지 않고 문제를 해결 한 방법입니다.

Spring 2.1.3Jackson 에서는 2019-05-21T07:37:11.000yyyy-MM-dd HH:mm:ss.SSS형식의 날짜 문자열이 에서 직렬화 해제 될 것으로 예상합니다 LocalDateTime. 확인 날짜 문자열로 날짜와 시간을 구분 T하지와를 space. 초 ( ss) 및 밀리 초 ( SSS)는 생략 할 수 있습니다.

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

0

fastjson 사용을 고려하면 문제를 해결할 수 있습니다. 버전을 확인하십시오.

 <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.56</version>
 </dependency>

0

GraphQL Java 도구로 인해이 문제가 발생 Instant하고 날짜 문자열에서 Java를 마샬링하려는 경우 특정 구성에서 ObjectMapper를 사용하도록 SchemaParser를 설정해야합니다.

GraphQLSchemaBuilder 클래스에서 ObjectMapper를 주입하고 다음 모듈을 추가하십시오.

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

옵션에 추가하십시오.

final SchemaParserOptions options = SchemaParserOptions.newOptions()
            .objectMapperProvider(fieldDefinition -> objectMapper)
            .typeDefinitionFactory(new YourTypeDefinitionFactory())
            .build();

https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/32를 참조 하십시오.


0

당신이 사용하는 경우 잭슨 시리얼을 , 여기에 날짜 모듈을 사용하는 방법은 다음과 같습니다

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.apache.kafka.common.serialization.Serializer;

public class JacksonSerializer<T> implements Serializer<T> {

    private final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new ParameterNamesModule())
            .registerModule(new Jdk8Module())
            .registerModule(new JavaTimeModule());

    @Override
    public byte[] serialize(String s, T object) {
        try {
            return mapper.writeValueAsBytes(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.