Java 8 LocalDate Jackson 형식


138

들어 java.util.Date 내가 할 때

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")  
  private Date dateOfBirth;

그런 다음 JSON 요청에서 보낼 때

{ {"dateOfBirth":"01/01/2000"} }  

효과가있다.

Java 8의 LocalDate 필드에 대해 어떻게해야합니까 ??

나는 노력했다

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;  

작동하지 않았다.

누군가 올바른 방법이 무엇인지 알려주세요 ..

아래는 종속성입니다

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>jaxrs-api</artifactId>
     <version>3.0.9.Final</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.2</version>
</dependency>
<dependency>
    <groupId>com.wordnik</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>1.3.10</version>
</dependency>

답변:


106

주석을 사용 하여이 작업을 간단하게 수행 할 수 없었습니다. 작동 시키려면 ContextResolverfor을 만든 ObjectMapper다음 write-date-as-time-stamp를 false로 설정 해야하는 한 가지 경고와 함께 JSR310Module( 업데이트 : 이제 JavaTimeModule대신 )를 추가했습니다. JSR310 모듈 설명서를 참조하십시오 . 여기 내가 사용한 것의 예가 있습니다.

의존

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

참고 :이 문제에 직면 한 한 가지 문제는 jackson-annotation버전 2.3.2를 사용하여 다른 종속성으로 가져온 버전 이 필요하다는 것 jsr310입니다. 일어난 일은 NoClassDefFound for ObjectIdResolver인데 2.4 클래스입니다. 포함 된 종속성 버전을 정렬해야했습니다.

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        // Now you should use JavaTimeModule instead
        MAPPER.registerModule(new JSR310Module());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

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

리소스 클래스

@Path("person")
public class LocalDateResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getPerson() {
        Person person = new Person();
        person.birthDate = LocalDate.now();
        return Response.ok(person).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createPerson(Person person) {
        return Response.ok(
                DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
    }

    public static class Person {
        public LocalDate birthDate;
    }
}

테스트

curl -v http://localhost:8080/api/person
결과: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person
결과: 2015-03-01


JAXB 솔루션 은 여기 를 참조하십시오 .

최신 정보

JSR310Module잭슨의 버전 2.7의 추천되지 않습니다. 대신 모듈을 등록해야합니다 JavaTimeModule. 여전히 동일한 종속성입니다.


1
HiDeskillet 필드 birthDate는 "birthDate": { "year": 0, "month": "Month", "dayOfMonth": 0, "dayOfWeek": "DayOfWeek", "era": { "로 생성됩니다. value ": 0},"dayOfYear ": 0,"leapYear ": false,"monthValue ": 0,"chronology ": {"id ":" ","calendarType ":" "}} 어떻게하면 바로 만들 수 있습니까? "birthDate"로 ???
JAB

ContextResolver가 호출되었는지 확인하십시오. getContext메소드에 인쇄 문을 추가하십시오 . 이 메소드가 호출되면 이것이 작동하지 않는 이유를 알 수 없습니다. 호출되지 않으면 앱 구성으로 수정해야 할 수도 있습니다. 이를 위해 당신이 제공 한 것보다 더 많은 것을 볼 필요가 있습니다. Resteasy 버전, 종속성, 앱 구성 web.xml 또는 응용 프로그램 하위 클래스와 같이. 기본적으로 문제를 재현하기에 충분
Paul Samsotha

ContextResolver를 Peeskillet이라고하지 않습니다. web.xml에서 <context-param> <param-name> resteasy.resources </ param-name> <param-value> com.bac.ObjectMapperContextResolver </ param-value> </ context-param>으로 등록하고 있습니다. 내가 사용하는 종속성에 대한 업데이트 된 질문
JAB

Swagger가 문제인 것 같습니다. 나는 그것을 비활성화하려고하지만 이 질문 에서 Swagger의 ObjectMapper와 충돌하여 자신의 것을 사용하려고 제기 된 문제가 있음을 알 수 있습니다. 그것들을 시도하고 비활성화 할 수 있으며 ContextResolver에서 모든 구성을 ObjectMapperswagger처럼 설정 하십시오 (질문에 링크가 표시됨). 나는 swagger를 많이 사용하지 않기 때문에 모른다. 그러나 나는 swagger가 주된 문제라고 생각하는데, contextresolver가 호출되지 않는 이유.
Paul Samsotha

추가 테스트 후 주석 작동합니다. 우리는하더라도 자신감 ObjectMapper를 사용하고 이미 우리를 위해 거짓으로 타임 스탬프를 구성합니다. 그래서 이것은 작동해야합니다. 더 나은 도움을 받으려면 문제를 보여주는 MCVE 를 제공하는 것이 좋습니다 .
Paul Samsotha

95

@JsonSerialize와 @JsonDeserialize는 나를 위해 잘 작동했습니다. 추가 jsr310 모듈을 가져올 필요가 없습니다.

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;

디시리얼라이저 :

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    protected LocalDateDeserializer() {
        super(LocalDate.class);
    }


    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        return LocalDate.parse(jp.readValueAs(String.class));
    }

}

시리얼 라이저 :

public class LocalDateSerializer extends StdSerializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    public LocalDateSerializer(){
        super(LocalDate.class);
    }

    @Override
    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider sp) throws IOException, JsonProcessingException {
        gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
    }
}

2
이것은 나에게 가장 좋은 대답입니다. 감사!
Romeo Jr Maranan 2016 년

7
해당 클래스는에 포함되어 jackson-datatype-jsr310있습니다. 프로젝트에서 수동으로 정의 할 필요가 없습니다.
NeuroXc

1
이 솔루션은의 직렬 변환기를 사용하여 저에게 효과적이었습니다 jackson-datatype-jsr310.
dave

2
이것이 새로운 최고의 답변이어야합니다.
Doctor Parameter

1
jackson-datatype-jsr310에서 serializer 및 deserializer를 사용하는 경우 @JsonFormat (shape = JsonFormat.Shape.STRING)을 필드에 추가하는 것이 좋습니다. 이 형식이 없으면 값은 [연도, 월, 일]로 직렬화되지만 직렬화 해제는 작동합니다.
지안 첸

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

나를 위해 잘 작동합니다.


2
new com.fasterxml.jackson.datatype.jsr310.JSR310Module()Jackson 버전 2.5.4 이 버전에는 JavaTimeModule 클래스가 없습니다.
user3774109

이 답변은 LocalDateTime(jackson 2.9.5)에도 적용됩니다. 1 개의 추가 종속성이 필요하므로 내 build.sbt는 다음과 같습니다."com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.5", "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.9.5"
ruhong

2
투표권이 더 있어야합니다. 간단하고 효과적입니다.
mkasberg

완벽하게 일했습니다! 감사합니다.
MAD-HAX

이것은 올바른 방향으로 나를 가리 켰습니다. 감사합니다! 나는 봄 부팅에 당신이 할 필요가 application.properties에 다음을 추가 것을 추가합니다 : spring.jackson.serialization.write - 날짜로서의 타임 스탬프를 = 거짓
주스트 Lambregts

46

Spring Boot 웹 앱에서 Jackson JSR 310 버전 "2.8.5"

compile "com.fasterxml.jackson.core:jackson-databind:2.8.5"
runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.5"

@JsonFormat작품 :

import com.fasterxml.jackson.annotation.JsonFormat;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate birthDate;

2
이 직렬화 해제 작동합니까? 아니면 직렬화 만? 직렬화
해제에

7
deserializer를 명시 적으로 선언해야했다@JsonDeserialize(using= LocalDateDeserializer.class)
rewolf

1
지금까지 가장 간단합니다!
Antonio

@JsonFormat출력 데이터 형식을 변경하기 위해. stackoverflow.com/a/53251526/816759는 완벽한 작동 @JsonFormat, @JsonDeserialize,@JsonSerialize
바하

당신이 JSR310 종속성을 추가하면 봄 부팅에서, 당신이 할 필요가 추가입니다 spring.jackson.serialization.write-dates-as-timestamps=false당신에 application.properties, 그리고 그것을 포맷을 yyyy-MM-dd자동으로. 필요 없음@JsonFormat
esfandia

31

역 직렬화 및 직렬화를 지원하는 가장 간단한 솔루션은 다음과 같습니다.

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate dateOfBirth;

프로젝트에서 다음과 같은 종속성을 사용하는 동안

메이븐

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.9.7</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.datatype</groupId>
   <artifactId>jackson-datatype-jsr310</artifactId>
   <version>2.9.7</version>
</dependency>

그래들

compile "com.fasterxml.jackson.core:jackson-databind:2.9.7"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7"

ContextResolver, Serializer 또는 Deserializer의 추가 구현이 필요하지 않습니다.


화려하고, 가장 멀리 떨어져 있습니다. 참고로 많은 의존성을 가진 사람에게는 잭슨 주석이 포함 된 다른 라이브러리를 업데이트해야했습니다.
brt

이 대답은 내 문제를 해결하기 위해 가장 가까운 것입니다. 직렬화는 작동하지만 @JsonFormat과 함께 사용한 패턴으로 인해 직렬화 해제가 실패합니다 (@JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy_HH : mm : SS")
fsakiyama

직렬화 해제에 실패한 ObjectMapper경우 JavaTimeModule등록 되지 않은 것 입니다. Spring / MessageConverter 프레임 워크에서 ObjectMapper 인스턴스가 제공되는 경우 그들은 그들을 연결하기 위해 약간의 마술을했다. 다른 경우에는 POJO의 모든 "LocalDate"에 대해 기본적 registerModule으로 사용하도록 설정 해야 합니다LocalDateDeserializer
Dennis C

19

이후 LocalDateSerializer오히려 "년 - 월 - 일"기본적으로 (JSON 문자열)보다 나는 특별한 요구하지 않으려는 이후에 회전을 "[년, 월, 일]"(JSON 배열) ObjectMapper당신이 할 수있는 (설정을 LocalDateSerializer비활성화하면 문자열 을 생성 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS하지만 추가 설정이 필요 하면 ObjectMapper), 다음을 사용합니다.

수입품 :

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

암호:

// generates "yyyy-MM-dd" output
@JsonSerialize(using = ToStringSerializer.class)
// handles "yyyy-MM-dd" input just fine (note: "yyyy-M-d" format will not work)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate localDate;

이제 new ObjectMapper()특별한 설정없이 객체를 읽고 쓰는 데 사용할 수 있습니다 .


3
내가 추가하고 싶은 한 가지는 오류 "2018-12-07"대신 날짜를 전달하는 것입니다 "2018-12-7".
Kid101

1
맞습니다 (1 자리 월 또는 일) 형식이 yyyy-MM-dd아닌 yyyy-M-d( 2 자리 월 및 일) 형식으로 작동 합니다.
Shadow Man

8

크리스토퍼의 업데이트에 대한 답변입니다.

버전 2.6.0 부터

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

사용 JavaTimeModule 대신 JSR310Module을 (사용되지 않음).

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        MAPPER.registerModule(new JavaTimeModule());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

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

설명서 에 따르면 , 새로운 JavaTimeModule은 동일한 표준 설정을 사용하여 표준 시간대 ID를 사용하지 않고 직렬화를 기본값으로 설정하고 대신 ISO-8601 호환 표준 시간대 오프셋 만 사용합니다.

SerializationFeature를 사용하여 동작을 변경할 수 있습니다 .WRITE_DATES_WITH_ZONE_ID


이것은 나를 도왔다. 내 경우에는 MAPPER.registerModule(new JavaTimeModule());줄 을 추가해야했습니다 . LocalDate 객체를 "2020-02-20"형식으로 포맷 할 수 있습니다. 내가 MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);찾던 것을 위해 줄을 필요로하지 않았습니다
Cuga

6

다음 주석이 잘 작동했습니다.

추가 종속성이 필요하지 않습니다.

    @JsonProperty("created_at")
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime createdAt;

5
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime createdDate;

4

https://stackoverflow.com/a/53251526/1282532 는 속성을 serialize / deserialize하는 가장 간단한 방법입니다. 이 접근 방식과 관련하여 두 가지 우려가 있습니다 .DRY 원칙을 어느 정도 위반하고 pojo와 mapper 사이의 높은 결합입니다.

public class Trade {
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate tradeDate;
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate maturityDate;
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate entryDate;
}

LocalDate 필드가 여러 개인 POJO가있는 경우 POJO 대신 매퍼를 구성하는 것이 좋습니다. https://stackoverflow.com/a/35062824/1282532 처럼 간단 할 수 있습니다.ISO-8601 값 ( "2019-01-31")을 사용하는 경우

사용자 정의 형식을 처리해야하는 경우 코드는 다음과 같습니다.

ObjectMapper mapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
mapper.registerModule(javaTimeModule);

논리는 한 번만 작성되며 여러 POJO에 재사용 할 수 있습니다.


3

지금까지 가장 간단하고 짧은 :

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;

스프링 부트> = 2.2 이상으로 종속성이 필요하지 않음


1

2020 및 Jackson 2.10.1 현재 특별한 코드가 필요하지 않으며 Jackson에게 원하는 것을 알려주는 것입니다.

ObjectMapper objectMapper = new ObjectMapper();

// Register module that knows how to serialize java.time objects
// Provided by jackson-datatype-jsr310
objectMapper.registerModule(new JavaTimeModule());

// Ask Jackson to serialize dates as String (ISO-8601 by default)
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

이것은 이미이 답변 에서 언급되었습니다 . 기능을 확인하는 단위 테스트를 추가하고 있습니다.

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.Data;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class LocalDateSerializationTest {

    @Data
    static class TestBean {
        // Accept default ISO-8601 format
        LocalDate birthDate;
        // Use custom format
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
        LocalDate birthDateWithCustomFormat;
    }

    @Test
    void serializeDeserializeTest() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();

        // Register module that knows how to serialize java.time objects
        objectMapper.registerModule(new JavaTimeModule());

        // Ask Jackson to serialize dates as String (ISO-8601 by default)
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // The JSON string after serialization
        String json = "{\"birthDate\":\"2000-01-02\",\"birthDateWithCustomFormat\":\"03/02/2001\"}";

        // The object after deserialization
        TestBean object = new TestBean();
        object.setBirthDate(LocalDate.of(2000, 1, 2));
        object.setBirthDateWithCustomFormat(LocalDate.of(2001, 2, 3));

        // Assert serialization
        assertEquals(json, objectMapper.writeValueAsString(object));

        // Assert deserialization
        assertEquals(object, objectMapper.readValue(json, TestBean.class));
    }
}

TestBean은 Lombok을 사용하여 Bean의 상용구를 생성합니다.


0

구성 클래스에서 LocalDateSerializerLocalDateDeserializer 클래스를 정의 하고 다음과 같이 JavaTimeModule을 통해 ObjectMapper에 등록하십시오 .

@Configuration
public class AppConfig
{
@Bean
    public ObjectMapper objectMapper()
    {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_EMPTY);
        //other mapper configs
        // Customize de-serialization


        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        mapper.registerModule(javaTimeModule);

        return mapper;
    }

    public class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(Constant.DATE_TIME_FORMATTER));
        }
    }

    public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {

        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return LocalDate.parse(p.getValueAsString(), Constant.DATE_TIME_FORMATTER);
        }
    }
}

0

요청에 다음과 같은 객체가 포함 된 경우 :

{
    "year": 1900,
    "month": 1,
    "day": 20
}

그런 다음 다음을 사용할 수 있습니다.

data class DateObject(
    val day: Int,
    val month: Int,
    val year: Int
)
class LocalDateConverter : StdConverter<DateObject, LocalDate>() {
    override fun convert(value: DateObject): LocalDate {
        return value.run { LocalDate.of(year, month, day) }
    }
}

필드 위 :

@JsonDeserialize(converter = LocalDateConverter::class)
val dateOfBirth: LocalDate

코드는 Kotlin에 있지만 물론 Java에서도 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.