Jackson @JsonSetter 활용

이번 시간엔 Java의 Jackson json library 중 @JsonSetter라는 annotation을 어떻게 사용해야 할지 알아보겠다.

public class Person {

  private String name;
  private Integer age = -1;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return age;
  }

  @JsonSetter(value = "age", nulls = Nulls.SKIP)
  public void setAge(Integer age) {
    this.age = age + 10;
  }
}

위와 같이 Person 클래스를 정의해보자. 필드는 간단하게 nameage 두개로 구성되어 있다. 사실 있는 값을 그대로 받아온다면 굳이 @JsonSetter는 사용할 필요가 없다. 하지만 만약 우리가 값에 어떤 특정 조작을 해야 한다면, 저 annotation은 매우 유용하다고 할 수 있다. 위의 경우엔 입력된 나이 값에 10을 더해서 값을 세팅하도록 해놨다.

여기서 신경써야할 몇가지 코너 케이스들이 있다.

  • Input json에 age 필드가 없을 경우
    • 이 경우엔 기본적으로 annotate되어있는 이 함수가 실행되지 않는다. 그래서 위에서 age=-1로 기본 값을 설정해 놓은 것이다.
  • age==null인 경우
    • 이 경우는 별도의 지시가 없으면 함수가 실행이 된다. 그런데 대부분의 경우 null이나 필드가 없는 경우나 동일시하는게 맞기 때문에, 이번 예제에서는 nulls=Nulls.SKIP을 사용함으로써, 값이 null일때는 함수를 실행하지 않도록 하였다. 그럼 결국 기본값인 -1을 유지할 것이다.

마지막으로 unit test를 써서 잘 동작하는지 확인해보자.

@Test
public void testDeserialization() throws IOException {
  String jsonStr = "{\n"
      + "  \"name\": \"Jacob\",\n"
      + "  \"age\": null\n"
      + "}";
  ObjectMapper mapper = new ObjectMapper();
  Person person = mapper.readValue(jsonStr, Person.class);
  assertEquals("Jacob", person.getName());
  assertEquals(-1L, person.getAge().longValue());

  jsonStr = "{\n"
      + "  \"name\": \"Jacob\",\n"
      + "  \"age\": 30\n"
      + "}";
  person = mapper.readValue(jsonStr, Person.class);
  assertEquals(40L, person.getAge().longValue());
}

Java에서 Json 다루기

Json(JavaScript Object Notation)은 기계와 인간이 소통하기에 가장 적합한 형태의 데이터 표시기법 중의 하나입니다. 특히 요즘과 같이 웹이 모든 일상을 지배하고 있는 이 시점에서 통일된 데이터 표시 규격은 너무나도 중요한 컨셉이지요. 그런 측면에서 Json은 어느 프로그래밍 언어를 사용하든 반드시 다루는 방법을 알고 계셔야 한다고 생각합니다. (참고 : http://www.json.org/json-ko.html)

제가 이번 예제에서 사용할 라이브러리는 jackson 이라는 가장 유명한 java 라이브러리 중 하나입니다. 일단 Json으로 표시할 데이터를 만들기 위해서 Java에서 class를 먼저 만들어 보도록 하겠습니다.

public class Person {

  private String name;
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

}

Person이라는 클래스를 만들었는데요, 2개의 field를 가지고 있습니다. 이름과 나이가 그것입니다. 그렇다면 이제 실제로 클래스를 만들고 이름으로 “Jacob”을, 나이로 27세를 입력하여 Json string으로 출력해 보도록 하겠습니다.

@Test
public void testWritePojoAsJsonString() {
  Person person = new Person();
  person.setName("Jacob");
  person.setAge(27);
  try {
    System.out.println(objectMapper.writeValueAsString(person));
  } catch (JsonProcessingException e) {
    e.printStackTrace();
  }
}

Pojo를 Json string으로 바꿔주는 함수가 바로 writeValueAsString이라는 함수인데요, 이 함수는 JsonProcessingException을 던지기 때문에 try catch를 사용하시든지 함수 자체에서 throw시키시든지 하셔야 합니다. 위의 함수를 실행했을 때 아래와 같이 출력됩니다.

{"name":"Jacob","age":27}

그러면 이제 저 Json string을 다시 Pojo로 바꾸고 싶으면 어떻게 하면 될까요?

@Test
public void testReadJsonStringAsPojo() {
  Person person;
  try {
    person = objectMapper.readValue("{\"name\":\"Jacob\",\"age\":27}", Person.class);
  } catch (IOException e) {
    e.printStackTrace();
    return; // When failed, quit the test
  }
  Assert.assertNotNull(person);
  Assert.assertEquals(person.getName(), "Jacob");
  Assert.assertEquals(person.getAge(), 27);
}

그것은 위와 같이 readValue라는 함수를 통해서 해당 Json 문자열이 어떤 class로 변환되면 되는지 명시해주면 됩니다. 이때도 마찬가지로 IOException을 handle해 주셔야 합니다. 가장 아래쪽의 3개의 Assert문을 통해서 Pojo가 제대로 생성되었음을 확인할 수 있습니다.

이번 예제에서는 가장 진짜 기본중의 기본기를 다뤘지만, 정말 필수적으로 알아두셔야 할 컨셉이고, 또한 이를 바탕으로 많은 고급 기능들을 구현해 나가실 수 있습니다 🙂