JodaTime 과 jackson json library 연동

 

설정

JodaTime 의 DateTime 클래스와 jackson 을 연동하여 사용하는 방법

public class User {
	private Long id;
	private String name;
	private DateTime created_at;
};

 

maven pom 설정

<properties> 
	<jackson.version>2.4.4</jackson.version> 
</properties>
 
<dependencies>		
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.version}</version>
		</dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
            <version>${jackson.version}</version>
        </dependency>
</dependencies>

Spring 연동

Spring3.2.x DateTimeFormat annotation 참고

Serialize

JsonSerialize annotation 을 도메인에 기술
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class User {
	private Long id;
	private String name;

	@JsonSerialize(using=DateTimeSerializer.class)
	@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Seoul")
	private DateTime createdAt;
};
User u = new User();
u.setId(1L);
u.setName("TestUser");
u.setCreatedAt(new DateTime());
 
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);		
mapper.writeValue(new File("user.json"), u);

 

DateTime 을 Serialize 할경우 다음 옵션이 추가되지 않으면 아래와 같이 불필요한 정보가 많이 추가된다.

mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
 Click here to expand...
{
    "created_at": {
        "weekyear": 2015,
        "monthOfYear": 4,
        "yearOfCentury": 15,
        "centuryOfEra": 20,
        "millisOfSecond": 289,
        "millisOfDay": 28518289,
        "secondOfMinute": 18,
        "secondOfDay": 28518,
        "minuteOfHour": 55,
        "minuteOfDay": 475,
        "weekOfWeekyear": 15,
        "yearOfEra": 2015,
        "hourOfDay": 7,
        "year": 2015,
        "dayOfMonth": 10,
        "dayOfWeek": 5,
        "era": 1,
        "dayOfYear": 100,
        "chronology": {
            "zone": {
                "fixed": false,
                "uncachedZone": {
                    "fixed": false,
                    "cachable": true,
                    "id": "Asia/Seoul"
                },
                "id": "Asia/Seoul"
            }
        },
        "millis": 1428620118289,
        "zone": {
            "fixed": false,
            "uncachedZone": {
                "fixed": false,
                "cachable": true,
                "id": "Asia/Seoul"
            },
            "id": "Asia/Seoul"
        },
        "afterNow": false,
        "beforeNow": true,
        "equalNow": false
    }
}

기본 시간 형식은 ISO 형식(2004-12-13T21:39:45.618-08:00) 이므로 @JsonFormat 으로 DateTime 형식을 기술해 준다.

 

DeSerialize

{
    "id": 1,
    "name": "TestUser",
    "createdAt": "2014-01-09 23:59:59"
}

위와 같이 json encoding 된 DateTime 을 DeSerialize 할 경우 ISO 형식이 아니므로 예외가 발생한다.

 

 Click here to expand...
ObjectMapper mapper = new ObjectMapper();


User u = mapper.readValue(new File("user.json"), User.class);
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class org.joda.time.DateTime] from String value ('2014-01-09 23:59:59'); no single-String constructor/factory method
 at [Source: status.json; line: 3, column: 9] (through reference chain: com.example.User["createdAt"]->com.example.User["createdAt"])
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
	at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:770)
	at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:277)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:289)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1141)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:135)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:126)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:538)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:238)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:538)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:238)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3066)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2115)

ISO 형식이 아닌 DateTime 을 파싱하기 위해서는 커스텀 DeSerializer 를 만들어야 한다.

public class CustomDateTimeDeserializer extends JsonDeserializer<DateTime> {	
	
    private static DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
	@Override
	public DateTime deserialize(JsonParser jp, DeserializationContext ctxt)
			throws IOException, JsonProcessingException {
		 String date = jp.getText();
		return formatter.parseDateTime(date);
	}
}
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class User {
	private Long id;
	private String name;

	@JsonSerialize(using=DateTimeSerializer.class)
	@JsonDeserialize(using=CustomDateTimeDeserializer.class)
	@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Seoul")
	private DateTime createdAt;
};

다시 다음 코드를 실행하면 정상적으로 동작할 것이다.

ObjectMapper mapper = new ObjectMapper();


User u = mapper.readValue(new File("user.json"), User.class);

JDBC 연동

JDBC 나 spring 의 JDBCTemplate 등에서 Joda Time 의 DateTime 클래스를 사용하려면 toDate() 로 java 의 Date 형식으로 변환해 주어야 함.( IterableNamedParameterJdbcTemplate  라이브러리는 https://github.com/alexkasko/springjdbc-iterable 참고).

IterableNamedParameterJdbcTemplate jdbcTemplate = iterableNamedParameterJdbcTemplate;
		
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime from = dtf.parseDateTime("2014-01-01 00:00:00");
DateTime to = dtf.parseDateTime("2014-03-31 23:59:59");
SqlParameterSource parameters = new MapSqlParameterSource()
                                .addValue("from", from.toDate())
                                .addValue("to", to.toDate())
                                ;
List<User> list = jdbcTemplate.query(
    "select * from USERS WHERE created_at > :from and created_at < :to ", 
    parameters,
    new BeanPropertyRowMapper(User.class));