티스토리 뷰
1. Overview
요즘은 API 서비스가 아니더라도 VIEW단과 백엔드가 통신을 할 때 JSON을 많이 이용합니다. 객체를 json으로 직렬화하거나 json 데이터를 java 객체에 역직렬화할 때 ObjectMapper는 매우 유용합니다.
2. Dependencies 추가
1
2
3
4
5
|
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
|
cs |
이미 spring-boot-starter-web을 디펜던시에 추가되었다면 별도 설정은 필요하지 않습니다.
3. 예제
Sample Object : User.java
@Getter
@Setter
@ToString
public class User {
private String userId;
private String userPwd;
private String name;
private String authType;
private Date timestamp;
private boolean isUpdate = false;
public User() {
super();
}
public User(String userId, String name, String authType) {
super();
this.userId = userId;
this.name = name;
this.authType = authType;
}
}
3-1. java Object to JSON
1
2
3
4
5
6
7
8
9
|
final String expected = "{\"userId\":\"userId\",\"userPwd\":null,\"name\":\"tester\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false}";
@Test
public void objectToJson() throws JsonProcessingException {
User user = new User("userId", "tester", "web");
ObjectMapper objectMapper = new ObjectMapper();
String userAsString = objectMapper.writeValueAsString(user);
assertThat(userAsString, equalTo(expected));
}
|
cs |
3-2. JSON to Java Object
Json String을 읽어서 Java 객체에 역직렬화하기
1
2
3
4
5
6
7
8
|
final String expected = "{\"userId\":\"userId\",\"userPwd\":null,\"name\":\"tester\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false}";
@Test
public void jsonToObject() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(expected, User.class);
assertThat(user.getUserId(), equalTo("userId"));
}
|
cs |
3-3. JSON 배열을 Java List에 매핑하기
1
2
3
4
5
6
7
8
9
10
11
12
13
|
final String expected1 = "{\"userId\":\"userId1\",\"userPwd\":null,\"name\":\"tester1\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false}";
final String expected2 = "{\"userId\":\"userId2\",\"userPwd\":null,\"name\":\"tester2\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false}";
final String listExpected = "["+expected1+","+expected2+"]";
@Test
public void jsonToListObject() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
List<User> users = objectMapper.readValue(listExpected, new TypeReference<List<User>>(){});
for(User user : users) {
System.out.println(user.toString());
assertThat(user.getName(), containsString("tester"));
}
}
|
cs |
3-4. JSON 데이터를 Map에 매핑하기
1
2
3
4
5
6
7
8
|
final String expected1 = "{\"userId\":\"userId1\",\"userPwd\":null,\"name\":\"tester1\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false}";
@Test
public void jsonToMap() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.readValue(expected1, new TypeReference<Map<String,Object>>(){});
assertThat(map.get("userId"), equalTo("userId1"));
}
|
cs |
4. 설정
4-1. 존재하지 않는 필드 무시 설정
기존에 JSON String에 새로운 필드를 추가하고 역직렬화를 실행하면 UnrecognizedPropertyException이 발생합니다.
실무에서 무수히 많은 데이터를 포함한 JSON 데이터에서 필요로 하는 것만 매핑할 때 많이 사용하는 옵션입니다.
간단하게 아래 한 줄만 추가해 주면 됩니다.
1
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 없는 필드로 인한 오류 무시
|
cs |
테스트
1
2
3
4
5
6
7
8
9
10
|
final String expected1 = "{\"userId\":\"userId1\",\"userPwd\":null,\"name\":\"tester1\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false,\"authGroup\":\"admin\" }";
@Test
public void objectToJson() throws JsonProcessingException {
User user = new User("userId1", "tester1", "web");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 없는 필드로 인한 오류 무시
String userAsString = objectMapper.writeValueAsString(user);
assertThat(userAsString, containsString("userId1"));
}
|
cs |
4-2. 사용자 정의 Serializer, Deserializer 사용하기
사용자 정의 직렬화 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class CustomUserSerializer extends StdSerializer<User>{
private static final long serialVersionUID = 1L;
public CustomUserSerializer() {
this(null);
}
public CustomUserSerializer(Class<User> user) {
super(user);
}
@Override
public void serialize(
User user, JsonGenerator jsonGenerator, SerializerProvider serializer) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("userId", user.getUserId());
jsonGenerator.writeStringField("userName", user.getName());
jsonGenerator.writeStringField("authType", user.getAuthType());
//jsonGenerator.writeNumberField("timestamp", (new Date()).getTime());
jsonGenerator.writeEndObject();
}
}
|
cs |
테스트
1
2
3
4
5
6
7
8
9
10
11
12
|
@Test
public void customObjectToJson() throws JsonProcessingException {
User user = new User("userId1", "tester1", "web");
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(User.class, new CustomUserSerializer());
objectMapper.registerModule(simpleModule);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 없는 필드로 인한 오류 무시
String userAsString = objectMapper.writeValueAsString(user);
assertThat(userAsString, equalTo("{\"userId\":\"userId1\",\"userName\":\"tester1\",\"authType\":\"web\"}"));
}
|
cs |
사용자 정의 역직렬화 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public class CustomUserDeserializer extends StdDeserializer<User>{
private static final long serialVersionUID = 1L;
public CustomUserDeserializer() {
this(null);
}
public CustomUserDeserializer(Class<?> user) {
super(user);
}
@Override
public User deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
User user = new User();
ObjectCodec codec = jsonParser.getCodec();
JsonNode node = codec.readTree(jsonParser);
JsonNode userNameNode = node.get("name");
JsonNode userIdNode = node.get("userId");
JsonNode authTypeNode = node.get("authType");
String userName = userNameNode.asText();
String userid = userIdNode.asText();
String authType = authTypeNode.asText();
user.setUserId(userid);
user.setName(userName);
user.setAuthType(authType);
return user;
}
}
|
cs |
테스트
1
2
3
4
5
6
7
8
9
10
11
12
|
final String expected1 = "{\"userId\":\"userId1\",\"userPwd\":null,\"name\":\"tester1\",\"authType\":\"web\",\"timestamp\":null,\"isUpdate\":false,\"authGroup\":\"admin\" }";
@Test
public void customJsonToObject() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(User.class, new CustomUserDeserializer());
objectMapper.registerModule(simpleModule);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 없는 필드로 인한 오류 무시
User user = objectMapper.readValue(expected1, User.class);
assertThat(user.getUserId(), equalTo("userId1"));
}
|
cs |
4.3 사용자 정의 ObjectMapper 활용
프로젝트 초기 네이밍 규칙 및 컨벤션에 따라 몇가지를 정의합니다.
예를 들어 화폐 등에서 세자리 마다 콤마를 사용하고 있다면 Integer, Long으로 정의된 프라퍼티에서 콤마는 제거하도록 deserializer를 만들어서 등록합니다. 날짜 포맷의 문자열인 경우 Date는 "yyyy-MM-dd"로 하고 Timestamp의 경우 "yyyy-MM-dd HH:mm:ss"로 처리한다고 정의를 합니다. 그에 따라 deserializer를 만들어서 CustomObjectMapper의 생성자에 만들어서 등록을 하고 모든 개발자는 이 매퍼를 사용하라고 가이드를 합니다.
User.java
날짜 형식에 따른 처리를 위해 Date type, Timestamp type 두개를 만듭니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
public class User implements Serializable{
private String userId;
private String userPwd;
private String name;
private String authType;
private Timestamp timestamp;
private Date date;
private boolean isUpdate = false;
...
...
}
|
cs |
CustomDateDeserializer
Date deserializer 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class CustomDateDeserializer extends JsonDeserializer<Date>{
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public CustomDateDeserializer() {
super();
}
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String valueAsString = jsonParser.getValueAsString();
if (StringUtils.isEmpty(valueAsString)) {
return null;
}
try {
SimpleDateFormat transFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
//transFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return transFormat.parse(valueAsString);
}catch(Exception e) {
return null;
}
}
}
|
cs |
CustomTimestampDeserializer
Timestamp deserializer 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class CustomTimestampDeserializer extends JsonDeserializer<Timestamp>{
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
public CustomTimestampDeserializer() {
super();
}
@Override
public Timestamp deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String valueAsString = jsonParser.getValueAsString();
if (StringUtils.isEmpty(valueAsString)) {
return null;
}
try {
SimpleDateFormat transFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
//transFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return new Timestamp(transFormat.parse(valueAsString).getTime());
}catch(Exception e) {
return null;
}
}
}
|
cs |
CustomObjectMapper
생성자에 프로젝트에서 사용하는 Serializer, Deserializer 등을 등록합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class CustomObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
public CustomObjectMapper(){
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Timestamp.class, new CustomTimestampDeserializer());
simpleModule.addDeserializer(Date.class, new CustomDateDeserializer());
// simpleModule.addDeserializer(Long.class, new LongDeserializer());
// simpleModule.addDeserializer(Integer.class, new IntegerDeserializer());
// simpleModule.addDeserializer(Float.class, new FloatDeserializer());
// simpleModule.addDeserializer(Double.class, new DoubleDeserializer());
registerModule(simpleModule);
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 없는 필드로 인한 오류 무시
}
}
|
cs |
테스트
1
2
3
4
5
6
7
8
|
final String expected3 = "{\"userId\":\"userId3\",\"userPwd\":null,\"name\":\"tester2\",\"authType\":\"web\",\"timestamp\":\"2019-03-04 13:59:59\",\"date\":\"2019-03-04\",\"isUpdate\":false,\"authGroup\":\"user\" }";
@Test
public void customObjectMapper() throws IOException {
ObjectMapper objectMapper = new CustomObjectMapper();
User user = objectMapper.readValue(expected3, User.class);
assertThat(user.getTimestamp().toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), equalTo("2019-03-04 13:59:59"));
}
|
cs |
참고
Intro to the Jackson ObjectMapper
- https://www.baeldung.com/jackson-object-mapper-tutorial
Github : https://github.com/kkaok/examples
'Spring Frameworks' 카테고리의 다른 글
Spring boot - RestTemplate 설정(Timeout, socketTimeOut) (2) | 2019.03.07 |
---|---|
Spring boot - Assert 사용 예제 (0) | 2019.03.05 |
Spring boot - Ehcache, cache 예제 (0) | 2019.02.26 |
Spring boot - 빌드 profile 설정 (0) | 2019.02.20 |
Spring Boot - 복수 데이터소스 설정하기 (0) | 2019.02.14 |
- Total
- Today
- Yesterday
- mapToList
- oracle
- 샘플
- Javascript
- UI
- cache
- ag grid
- Spring Boot
- thymeleaf
- 타임리프
- REST
- lombok
- java
- sample
- spring
- 엑셀
- mybatis
- 예제
- RESTful
- 스프링
- example
- listToMap
- 그리드
- 설정
- 메시지
- 스프링부트
- SHEETJS
- AG-GRID
- restful서비스
- springboot
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |