[java] JSON Jackson으로 날짜 형식 매핑

API에서 오는 날짜 형식이 다음과 같습니다.

"start_time": "2015-10-1 3:00 PM GMT+1:00"

YYYY-DD-MM HH : MM am / pm GMT 타임 스탬프입니다. 이 값을 POJO의 Date 변수에 매핑하고 있습니다. 분명히 변환 오류를 표시합니다.

두 가지를 알고 싶습니다.

  1. Jackson으로 변환하기 위해 사용해야하는 형식은 무엇입니까? Date가 이것에 적합한 필드 유형입니까?
  2. 일반적으로 Jackson이 객체 멤버에 매핑하기 전에 변수를 처리하는 방법이 있습니까? 형식 변경, 계산 등


답변

Jackson으로 변환하기 위해 사용해야하는 형식은 무엇입니까? Date가 이것에 적합한 필드 유형입니까?

Date이에 대한 훌륭한 필드 유형입니다. 다음을 사용하여 JSON 구문 분석 가능을 쉽게 만들 수 있습니다 ObjectMapper.setDateFormat.

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);

일반적으로 Jackson이 객체 멤버에 매핑하기 전에 변수를 처리하는 방법이 있습니까? 형식 변경, 계산 등

예. JsonDeserializer예를 들어 확장과 같은 사용자 정의 구현을 포함하여 몇 가지 옵션이 있습니다 JsonDeserializer<Date>. 이것은 좋은 시작입니다.


답변

Jackson v2.0부터 @JsonFormat 주석을 Object 멤버에서 직접 사용할 수 있습니다.

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;


답변

물론 직렬화 및 역 직렬화라는 자동화 된 방법 이 있으며 특정 주석 ( @JsonSerialize , @JsonDeserialize)으로 정의 할 수 있습니다. 언급 )으로 있습니다.

java.util.Date 및 java.util.Calendar …와 아마도 JodaTime을 모두 사용할 수 있습니다.

역 직렬화 (직렬화가 완벽하게 작동 ) 중에 @JsonFormat 주석이 원하는대로 작동하지 않았습니다 ( 시간대 를 다른 값으로 조정 했습니다).

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET")

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest")

예측 된 결과를 원하면 @JsonFormat 주석 대신 사용자 정의 직렬 변환기 및 사용자 정의 디시리얼라이저를 사용해야합니다. 여기에서 훌륭한 튜토리얼과 솔루션을 찾았습니다.http://www.baeldung.com/jackson-serialize-dates에서 .

Date 필드 에는 예제가 있지만 Calendar 필드에는 필요 하므로 구현은 다음과 같습니다.

시리얼 라이저 클래스 :

public class CustomCalendarSerializer extends JsonSerializer<Calendar> {

    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU");
    public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest");

    @Override
    public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        if (value == null) {
            gen.writeNull();
        } else {
            gen.writeString(FORMATTER.format(value.getTime()));
        }
    }
}

디시리얼라이저 클래스 :

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> {

    @Override
    public Calendar deserialize(JsonParser jsonparser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String dateAsString = jsonparser.getText();
        try {
            Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString);
            Calendar calendar = Calendar.getInstance(
                CustomCalendarSerializer.LOCAL_TIME_ZONE,
                CustomCalendarSerializer.LOCALE_HUNGARIAN
            );
            calendar.setTime(date);
            return calendar;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

위 클래스 의 사용법 :

public class CalendarEntry {

    @JsonSerialize(using = CustomCalendarSerializer.class)
    @JsonDeserialize(using = CustomCalendarDeserializer.class)
    private Calendar calendar;

    // ... additional things ...
}

이 구현을 사용하면 직렬화 및 역 직렬화 프로세스를 연속적으로 실행하면 원점 값이 생성됩니다.

@JsonFormat 주석 만 사용하면 deserialization은 라이브러리 내부 표준 시간대 기본 설정 때문에 주석 매개 변수로 변경할 수없는 결과 (Jackson 라이브러리 2.5.3 및 2.6.3 버전에서도 경험 한 결과)가 다른 결과를 낳습니다 .


답변

날짜 시간 형식의 스프링 부트 응용 프로그램에 대한 완벽한 예RFC3339

package bj.demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;

import java.text.SimpleDateFormat;

/**
 * Created by BaiJiFeiLong@gmail.com at 2018/5/4 10:22
 */
@SpringBootApplication
public class BarApp implements ApplicationListener<ApplicationReadyEvent> {

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

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
    }
}


답변

날짜에 T 및 Z와 같은 문자를 추가하려면

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private Date currentTime;

산출

{
    "currentTime": "2019-12-11T11:40:49Z"
}


답변

나를 위해 일하고 있습니다. 스프링 부트.

 import com.alibaba.fastjson.annotation.JSONField;

 @JSONField(format = "yyyy-MM-dd HH:mm:ss")
 private Date createTime;

산출:

{
   "createTime": "2019-06-14 13:07:21"
}


답변

@ miklov-kriven의 매우 유용한 답변을 바탕 으로이 두 가지 추가 고려 사항이 누군가에게 도움이되기를 바랍니다.

(1) serializer와 de-serializer를 동일한 클래스의 정적 내부 클래스로 포함시키는 것이 좋습니다. NB, ThreadD를 사용하여 SimpleDateFormat의 스레드 안전성.

public class DateConverter {

    private static final ThreadLocal<SimpleDateFormat> sdf =
        ThreadLocal.<SimpleDateFormat>withInitial(
                () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});

    public static class Serialize extends JsonSerializer<Date> {
        @Override
        public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception {
            if (value == null) {
                jgen.writeNull();
            }
            else {
                jgen.writeString(sdf.get().format(value));
            }
        }
    }

    public static class Deserialize extends JsonDeserializer<Date> {
        @Overrride
        public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception {
            String dateAsString = jp.getText();
            try {
                if (Strings.isNullOrEmpty(dateAsString)) {
                    return null;
                }
                else {
                    return new Date(sdf.get().parse(dateAsString).getTime());
                }
            }
            catch (ParseException pe) {
                throw new RuntimeException(pe);
            }
        }
    }
}

(2) 각 개별 클래스 멤버에서 @JsonSerialize 및 @JsonDeserialize 주석을 사용하는 대신 애플리케이션 레벨에서 사용자 정의 직렬화를 적용하여 Jackson의 기본 직렬화를 대체 할 수도 있습니다. 즉, 날짜 유형의 모든 클래스 멤버가 Jackson에 의해 직렬화됩니다. 각 필드에 명시적인 주석없이이 사용자 지정 직렬화를 사용합니다. 예를 들어 Spring Boot를 사용하는 경우 한 가지 방법은 다음과 같습니다.

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

    @Bean
    public Module customModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Date.class, new DateConverter.Serialize());
        module.addDeserializer(Date.class, new Dateconverter.Deserialize());
        return module;
    }
}