소개
- Jackson은 자바에서 JSON(Javascript Objet Notation)을 사용하기 위해 사용하는 라이브러리 중 하나입니다.
- 직렬화(serialization)는 데이터를 전송하거나 저장하기 위해 데이터를 적절한 포맷으로 변환하는 과정입니다. 프로그래밍 언어나 플랫폼에 독립적이고 표준화된 포맷이 직렬화에 많이 쓰이는데, JSON은 이런 포맷 중 하나입니다.
- 프로젝트 내 Jackson Library 사용 중 LocalDateTime 포맷 직렬화 중 발생한 에러 조치 방법을 공유하고자 합니다.
- Jackson 라이브러리 관련 내용은 아래 링크를 참조 바랍니다.
https://github.com/FasterXML/jackson
이 글의 간략한 PROCESS
- 자바8부터 지원하는 LocalDateTime, LocalDate를 제외한 다른 데이터 타입 필드를 가지는 객체 생성
- Serialize → Deserialize 작동 여부 확인
- LocalDateTime과 LocalDate를 객체에 추가
- Serialize → Deserialize 작동 여부 확인(오류 발생 확인)
- 해결 방법 공유
Jackson 사용방법
라이브러리는 Maven Repository를 통해 받을 수 있습니다.
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
다양한 데이터 타입이 정상 작동하는지 여부를 확인하기 위해 다양한 타입의 필드를 가진 SampleObject 클래스와 테스트를 위한 간단한 메인 클래스를 만들었습니다.
package mech2cs;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SampleObject {
public int int_ = 0;
public Integer integerWrapper_ = Integer.valueOf(0);
public String string_ = "testString";
public double double_ = 0.0;
public Double doubleWrapper_ = Double.valueOf(0.0);
public float float_ = 0l;
public Float floatWrapper_ = Float.valueOf(0l);
public char char_ = 'a';
public Character characterWrapper_ = Character.valueOf('a');
public List<String> list_ = Arrays.asList("a", "b", "c");;
public Map<String, String> map_ = Map.of("key1","value1","key2","value2");;
public Set<String> set_ = Set.of("a","b");;
}
package mech2cs;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
// 다양한 데이터 필드를 가진 객체 생성
SampleObject sampleObject = new SampleObject();
// Jackson Library를 활용한 {JAVA 객체} -> {JSON} 변환
ObjectMapper objectMapper = new ObjectMapper();
String serializedStr = null;
try {
serializedStr = objectMapper.writeValueAsString(sampleObject);
System.out.println(serializedStr);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// Jackson Library를 활용한 {JSON} -> {JAVA 객체} 변환
try {
SampleObject resultObject = objectMapper.readValue(serializedStr, SampleObject.class);
} catch (JsonProcessingException e) {
System.out.println("Deserialize Failed");
e.printStackTrace();
}
}
}
클래스 실행 결과는 아래와 같으며, 모든 데이터 타입들이 Json 형태로 잘 저장되는 것을 알 수 있습니다.
// 보기 편하게 줄바꿈
{
"int_":0,
"integerWrapper_":0,
"string_":"sampleString",
"double_":0.0,
"doubleWrapper_":0.0,
"float_":0.0,
"floatWrapper_":0.0,
"char_":"a",
"characterWrapper_":"a",
"list_":["a","b","c"],
"map_":{"key2":"value2","key1":"value1"},
"set_":["a","b"]
}
하지만 아래와 같이 SampleObject에 LocalDateTime과 LocalDate를 추가한 후 Main 메서드를 실행하게 되면 com.fasterxml.jackson.databind.exc.InvalidDefinitionException 에러가 발생하게 됩니다.
package mech2cs;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SampleObject {
public int int_ = 0;
public Integer integerWrapper_ = Integer.valueOf(0);
public String string_ = "testString";
public double double_ = 0.0;
public Double doubleWrapper_ = Double.valueOf(0.0);
public float float_ = 0l;
public Float floatWrapper_ = Float.valueOf(0l);
public char char_ = 'a';
public Character characterWrapper_ = Character.valueOf('a');
public List<String> list_ = Arrays.asList("a", "b", "c");;
public Map<String, String> map_ = Map.of("key1","value1","key2","value2");;
public Set<String> set_ = Set.of("a","b");;
// 새롭게 추가된 필드
public LocalDateTime localDateTime_ = LocalDateTime.now();
public LocalDate localDate_ = LocalDate.now();
}

해결방법
Jackson은 자바 8부터 추가된 LocalDateTime과 LocalDate 타입을 지원하기 위해 몇가지 모듈을 추가하였습니다 (아래 링크 참조). 모듈을 사용하기 위해 추가로 maven dependecies를 추가해야 합니다.
https://github.com/FasterXML/jackson-modules-java8
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.3</version>
</dependency>
위 링크에서 추천하는 방법을 통해 ObjectMapper에 모듈을 추가하는 코드를 넣고 실행하면 정상 결과를 출력하는 것을 볼 수 있습니다. (모듈 추가 방법1과 방법2 중 선택 가능합니다)
package mech2cs;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
public class Main {
public static void main(String[] args) {
// 다양한 데이터 필드를 가진 객체 생성
SampleObject sampleObject = new SampleObject();
// Jackson Library를 활용한 {JAVA 객체} -> {JSON} 변환
// 모듈 추가 방법 1
ObjectMapper objectMapper = JsonMapper.builder()
.addModule(new ParameterNamesModule())
.addModule(new Jdk8Module())
.addModule(new JavaTimeModule())
.build(); // 모듈 추가
// 모듈 추가 방법 2
// ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
String serializedStr = null;
try {
serializedStr = objectMapper.writeValueAsString(sampleObject);
System.out.println(serializedStr);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// Jackson Library를 활용한 {JSON} -> {JAVA 객체} 변환
try {
SampleObject resultObject = objectMapper.readValue(serializedStr, SampleObject.class);
} catch (JsonProcessingException e) {
System.out.println("Deserialize Failed");
e.printStackTrace();
}
}
}
{
...
"localDateTime_":[2021,5,31,1,24,50,401107591],
"localDate_":[2021,5,31]
}