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

구성 클래스에서 LocalDateSerializer 및 LocalDateDeserializer 클래스를 정의 하고 다음과 같이 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.