개발을 진행하다가 문득 "Jackson으로 JSON을 읽거나 쓸 때, DateTime 형은 어떤 포멧을 가질까? 그리고 이를 원하는 포멧으로 지정할 수 있을까?” 라는 의문이 생겼다.
그냥 무작정 해보자. DateTime 형 속성을 하나 갖고 있는 모델이 있다고 가정하자.
@Data
@AllArgsConstructor
public class DateTimeModel {
DateTime ymdt;
}
|
public class TempTest {
private ObjectMapper objectMapper = new ObjectMapper();
@Test
public void deserialize() throws Exception {
DateTimeModel model = new DateTimeModel(new DateTime());
System.out.println(objectMapper.writeValueAsString(model));
}
@Test
public void serialize() throws Exception {
String json = "{\"ymdt\": \"2017-07-27 23:59:59\"}";
DateTimeModel model = objectMapper.readValue(json, DateTimeModel.class);
System.out.println(model);
}
}
|
TC를 수행해 보면 output은 아래와 같이 나온다.
{"ymdt":{"weekOfWeekyear":30,"millisOfDay":80139861,"monthOfYear":7,"hourOfDay":22,"minuteOfHour":15,"secondOfMinute":39,"millisOfSecond":861,"weekyear":2017,"yearOfEra":2017,"yearOfCentury":17,"centuryOfEra":20,"secondOfDay":80139,"minuteOfDay":1335,"era":1,"dayOfYear":208,"dayOfWeek":4,"dayOfMonth":27,"year":2017,"chronology":{"zone":{"fixed":false,"uncachedZone":{"fixed":false,"cachable":true,"id":"Asia/Seoul"},"id":"Asia/Seoul"}},"zone":{"fixed":false,"uncachedZone":{"fixed":false,"cachable":true,"id":"Asia/Seoul"},"id":"Asia/Seoul"},"millis":1501161339861,"afterNow":false,"beforeNow":true,"equalNow":false}} |
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.joda.time.DateTime: no String-argument constructor/factory method to deserialize from String value ('2017-07-27 23:59:59')
at [Source: {"ymdt": "2017-07-27 23:59:59"}; line: 1, column: 10] (through reference chain: com.naver.storeplatform.api.common.DateTimeModel["ymdt"])
|
DateTime 형의 모든 속성이 Json에 담기는 듯 하다.
보통 날짜를 표현할 때 "년-월-일 시:분:초” 형식으로 많이 사용할 것이다.(한국시간 기준)
DateTime 포멧을 지정할 순 없을까?
ObjectMapper 생성시 module로서 DateTime Serializer/Deserializer를 등록할 수 있다.
아래와 같이 DateTimeFormatModule을 작성하고 ObjectMapper에 module을 등록해 주면 된다.
public class DateTimeFormatModule extends SimpleModule {
public DateTimeFormatModule() {
super();
addSerializer(DateTime.class, new JsonSerializer<DateTime>() {
public final DateTimeZone TIME_ZONE = DateTimeZone.forID("Asia/Seoul");
public final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(TIME_ZONE);
@Override
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if (value == null) {
gen.writeNull();
} else {
gen.writeString(FORMATTER.print(value));
}
}
});
addDeserializer(DateTime.class, new JsonDeserializer<DateTime>() {
public final DateTimeZone TIME_ZONE = DateTimeZone.forID("Asia/Seoul");
public final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(TIME_ZONE);
@Override
public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT) {
return new DateTime(p.getLongValue(), TIME_ZONE);
}
if (t == JsonToken.VALUE_STRING) {
String str = p.getText();
if (StringUtils.isEmpty(str)) {
return null;
}
return FORMATTER.parseDateTime(str);
}
throw ctxt.mappingException(DateTime.class);
}
});
}
}
|
public class TempTest {
private ObjectMapper objectMapper = new ObjectMapper();
@Before public void setUp() {
objectMapper.registerModule(new DateTimeFormatModule());
}
@Test public void deserialize() throws Exception {
DateTimeModel model = new DateTimeModel(new DateTime());
System.out.println(objectMapper.writeValueAsString(model));
}
@Test public void serialize() throws Exception {
String json = "{\"ymdt\": \"2017-07-27 23:59:59\"}";
DateTimeModel model = objectMapper.readValue(json, DateTimeModel.class);
System.out.println(model);
}
}
|
그런 다음 TC를 다시 수행해 보면 아래와 같이 결과가 잘 나오는 것을 확인할 수 있다.
{"ymdt":"2017-07-27 22:24:01"} |
DateTimeModel(ymdt=2017-07-27T23:59:59.000+09:00) |
즉, Serializer/Deserializer를 만들어서 ObjectMapper에 Module로 등록하면 POJO의 속성이 해당 타입인 경우 등록된 Serializer/Deserializer로 수행되는 것이다.
이를 활용하면 Spring Controller에서 @RequestParam이나 @ResponseBody에 사용될 날짜형 속성들에 대해서 포멧을 임의로 지정해 줄 수도 있다.
참고
- http://www.joda.org/joda-time/quickstart.html
- https://github.com/FasterXML/jackson
- http://programming.kilanbot.com/index.php/programming/30500-Date-format-Mapping-to-JSON-Jackson
- http://d2.naver.com/helloworld/645609
- http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTime.html
'Programing > Java' 카테고리의 다른 글
How to use exceptions effectively? (어떻게 Exception을 효율적으로 사용할까?) (0) | 2017.08.28 |
---|---|
Java에서 String과 new String()의 차이는? (2) | 2017.08.24 |
Jackson을 이용하여 JSON -> POJO 변환시 POJO에 존재하지 않는 property가 있는 경우 (0) | 2017.07.21 |
상속 구조의 모델 객체에서 Lombok의 @Builder, @Delegate 사용기 (0) | 2017.04.21 |
Lombok @Builder 사용시 기본값 지정하기 (0) | 2017.04.12 |