Json을 파싱할 수 있는 ObjectMapper 예시를 소개한다.  


많이 알려져 있지만, 자주 실수할 수 있는 내용과  좋은 팁을 소개한다. 





먼저, UnrecognizedPropertyException을 많이 발생시킬 수 있는 내용을 소개한다. 


아래는 무난하게 잘 동작하는 코드이다. 


import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonTest {

  @Test

  public void mapperObject() throws Exception {

  ObjectMapper mapper = new ObjectMapper();

  String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\"}";

  EventData eventData = mapper.readValue(jsonInString, EventData.class);

  System.out.println(eventData);

  }

}

 

class EventData {


  @JsonProperty("event_code")

  private int eventCode;


  @JsonProperty("name")

  private String name;


  @JsonProperty("messages")

  private List<String> messages;

}




이 코드가 정상적으로 잘 동작하려면, json이 EventData에 맞게 오지 않거나 최소한 해당 json 노드이외에는 전달되지 않는다는 보장이 있어야 한다. 


갑자기 json 문자열에 새로운 노드가 새로 추가되면, com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException이 발생한다.


String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\", \"emp\":\"samuel\"}";




예외 내용은 다음과 같다. 


com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "emp" (class com.kakao.story.util.EventData), not marked as ignorable (3 known properties: "event_code", "name", "messages"])

 at [Source: {"event_code":112233,"messages":["hello","java"],"name":"x112x1121", "emp":"samuel"}; line: 1, column: 77] (through reference chain: com.kakao.story.util.EventData["emp"])

at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)

at 





예외를 발생시키지 않고 안전하게 해결하려면 @JsonIgnoreProperties(ignoreUnknown = true)을 클래스 정의에 둔다. 다른 방법도 있지만, 이 방식이 아주 많이 사용되는 방식이다.



import java.util.List;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\", \"emp\":\"samuel\"}";

EventData eventData = mapper.readValue(jsonInString, EventData.class);

System.out.println(eventData);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

class EventData {


@JsonProperty("event_code")

private int eventCode;


@JsonProperty("name")

private String name;


@JsonProperty("messages")

private List<String> messages;

}




참고로, 코드는 문제가 없어보이지만, org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "event_code" (Class com.kakao.story.util.EventData), not marked as ignorable 예외가 발생할 수 있다.


ObjectMapper 클래스의 fully qualified name을 유심히 살펴본다. 


import org.codehaus.jackson.map.ObjectMapper;


import java.util.List;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.DeserializationConfig;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\", \"emp\":\"samuel\"}";

EventData eventData = mapper.readValue(jsonInString, EventData.class);

System.out.println(eventData);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

class EventData {


@JsonProperty("event_code")

private int eventCode;


@JsonProperty("name")

private String name;


@JsonProperty("messages")

private List<String> messages;

}



import com.fasterxml.jackson.databind.ObjectMapper를 사용해야지 org.codehaus.jackson.map.ObjectMapper를 사용하면 원인모를 미궁에 빠질 수 있다. 






또 다른 팁은 serialization시 null에 대한 핸들링이다. 아래 코드를 살펴본다.



import java.util.List;


import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

EventData eventData = new EventData(); 

eventData.eventCode = 123123;

eventData.name = "wang.lee";

String jsonString = mapper.writeValueAsString(eventData);

System.out.println(jsonString);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

class EventData {


@JsonProperty("event_code")

int eventCode;


@JsonProperty("name")

String name;


@JsonProperty("messages")

List<String> messages;

@Override

public String toString() {

return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);

}

}


실행하면, 저장 안한 키의 값을 null로 serialization한다. 


{"event_code":123123,"name":"wang.lee","messages":null}


최적화된 json 정보를 유지하기 위해 null 정보를 뺄 수 있다.




Pojo 클래스에 @JsonInclude(Include.NON_NULL)을 추가하면, null 정보를 뺄 수 있다.

import java.util.List;


import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.annotation.JsonInclude;

import static com.fasterxml.jackson.annotation.JsonInclude.Include;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

EventData eventData = new EventData(); 

eventData.eventCode = 123123;

eventData.name = "wang.lee";

String jsonString = mapper.writeValueAsString(eventData);

System.out.println(jsonString);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

//@JsonInclude(Include.NON_NULL)
@JsonInclude(value=Include.NON_ABSENT, content=Include.NON_EMPTY)

class EventData {


@JsonProperty("event_code")

int eventCode;


@JsonProperty("name")

String name;


@JsonProperty("messages")

List<String> messages;

@Override

public String toString() {

return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);

}

}


결과는 다음과 같다. 


{"event_code":123123,"name":"wang.lee"}



Java8과 guava에서도 쓸 수 있게 @JsonInclude(value=Include.NON_ABSENT, content=Include.NON_EMPTY)를 사용했다. java7이하에서는 @JsonInclude(Include.NON_NULL)를 사용할 수 있을 것이다. 





참고로 @JsonInclude에 대해 설명하면 다음과 같다.


Include.NON_ABSENT는 값이 null이나 java8, guava의 optional의 내부값이 null일 때 serialization하지 않는다.

Include.ALWAYS는 값과 상관없이 serialization한다.

Include.NON_DEFAULT는 값이 기본 설정과 다른 경우 속성이 serialization한다.

Include.NON_EMPTY는 값이 null이 아니거나 empty하지 않으면 serialization한다.

Include.NON_NULL는 null이 아닐 때만 serialzation한다.






Posted by '김용환'
,