Jacksonの使い方

依存の取得

Gradleの場合は以下。Java8のOptionalを使う場合は、jackson-datatype-jdk8を入れる。

dependencies {
  /* Jackson */
  implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9.1'
  // which depends on
  // implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.9'
  // implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.0'

  /* Supports Optional */
  implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jdk8', version: '2.9.9'

  /* for Unit test */
  testImplementation group: 'junit', name: 'junit', version: '4.12'

基本的な使い方

以下のユニットテストを起動してみれば一目瞭然と思う。

import java.util.*;
import java.util.stream.*;

import org.junit.*;
import static org.junit.Assert.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.type.*;
import com.fasterxml.jackson.databind.*;

public class SimpleTest {

  String DATA = "{\r\n" + 
      "    \"id\": 1,\r\n" + 
      "    \"name\": \"鈴木\",\r\n" + 
      "    \"hoby\": [\r\n" + 
      "        \"fishing\",\r\n" + 
      "        \"映画\"\r\n" + 
      "    ]\r\n" + 
      "}";

  String DATA_ARRAY = "[" + DATA + "," + DATA + "]";

  @Test
  public void readTest1() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    assertEquals("id:1, name:鈴木, hoby:fishing,映画", 
        mapper.readValue(DATA, Person1.class).toString());    
    try {
      mapper.readValue(DATA,  Person2.class);
      fail();
    } catch (Exception ex) {
    }    
    mapper.readValue(DATA,  Person3.class);   
    assertEquals("id:1, name:鈴木, hoby:fishing,映画", 
        mapper.readValue(DATA, Person4.class).toString()); 
  }

  @Test
  public void readTest2() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.readValue(DATA, Person1.class);
    mapper.readValue(DATA, Person2.class);
    mapper.readValue(DATA, Person3.class);
  }

  @Test
  public void readTest3() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    List<Person1>list =  mapper.readValue(DATA_ARRAY, new TypeReference<List<Person1>>() {});
    assertEquals(
      "id:1, name:鈴木, hoby:fishing,映画\n" + 
      "id:1, name:鈴木, hoby:fishing,映画",
      list.stream().map(p->p.toString()).collect(Collectors.joining("\n")));
  }

  @Test
  public void writeTest1() throws Exception {
    Person1 p = new Person1();
    p.id = 123;
    p.name = null;
    ObjectMapper mapper = new ObjectMapper();
    assertEquals("{\"id\":123,\"name\":null,\"hoby\":null}", mapper.writeValueAsString(p));    
  }


  static class Person1 {
    public int id;
    public String name;
    public List<String> hoby;

    @Override
    public String toString() {
      return "id:" + id + 
          ", name:" + name + 
          ", hoby:" + hoby.stream().collect(Collectors.joining(","));      
    }
  }

  static class Person2 {
    public int id;
    public String name;
  }

  @JsonIgnoreProperties(ignoreUnknown=true)
  static class Person3 {
    public int id;
    public String name;
  }

  static class Person4 {
    public int id;
    public String name;
    public String[]hoby;

    @Override
    public String toString() {
      return "id:" + id + 
          ", name:" + name + 
          ", hoby:" + Arrays.stream(hoby).collect(Collectors.joining(","));      
    }
  }
}

Optionalを使う場合


import static org.junit.Assert.*; import java.util.*; import java.util.stream.*; import org.junit.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.datatype.jdk8.*; public class OptionalTest { String DATA = "{\r\n" + " \"id\": 1,\r\n" + " \"name\": \"鈴木\",\r\n" + " \"hoby\": [\r\n" + " \"fishing\",\r\n" + " \"映画\"\r\n" + " ]\r\n" + "}"; @Test public void test() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Jdk8Module()); Person1 p = mapper.readValue(DATA, Person1.class); assertEquals("id:1, name:Optional[鈴木], hoby:fishing,映画", p.toString()); assertEquals("null", "" + p.remarks1); assertEquals("Optional.empty", "" + p.remarks2); } static class Person1 { public int id; public Optional<String> name; public List<String> hoby; public Optional<String>remarks1; public Optional<String>remarks2 = Optional.empty(); @Override public String toString() { return "id:" + id + ", name:" + name + ", hoby:" + hoby.stream().collect(Collectors.joining(",")); } } }

まとめ

不明プロパティへの対応

将来的にプロパティが増えても困らないように、ObjectMapper自体に指定しておいた方がよい。

    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

もちろん、「フィールド名を勘違いしていた」場合に無視されてしまうのだが、テスト段階で気づけばよいことだ。

Optionalへの対応

Java8のOptionalを使う場合には、Optionalは必ずOptional.empty()で初期化しておかないといけない。フィールドが存在しなくても勝手にOptional.empty()を入れてはくれない。