LoginSignup
7
4

More than 5 years have passed since last update.

Jacksonでインタフェースと実装クラスがわかれているときにデシリアライズする方法

Last updated at Posted at 2014-10-08

インタフェースクラス(Child.class)と、実装クラス(ChildImpl.class)がわかれているとき、そのままデシリアライズしようとすると、

public class DeserTest {

  static interface Child {
    public String getMemo();
    public void setMemo(String memo);
  }

  static class ChildImpl implements Child {
    private String memo;

    public String getMemo() {  return memo;  }
    public void setMemo(String memo) {  this.memo = memo;  }
  }

  static class HogeBean {
    private int id;
    private String name;
    private URL url;
    private List<Child> list;
    private Map<Integer, Child> map;

    public List<Child> getList() {  return list;  }
    public void setList(List<Child> list) {  this.list = list;  }
    public Map<Integer, Child> getMap() {  return map;  }
    public void setMap(Map<Integer, Child> map) {  this.map = map;  }
    public int getId() {  return id;  }
    private void setId(int id) {  this.id = id;  }
    public String getName() {  return name;  }
    public void setName(String name) {  this.name = name;  }
    public URL getUrl() {  return url;  }
    public void setUrl(URL url) {  this.url = url;  }
  }
  public static void main(String[] args) throws Exception {
    String text =
      "{\"list\":[{\"memo\":\"めもですが\"},{\"memo\":\"めもですが22\"},{\"memo\":123}],"
          + "\"id\":-773504340,"
          + "\"name\":\"あい うえ男\","
          + "\"url\":\"http://www.hoge.co.jp\","
          + "\"map\":{\"999\":{\"memo\":\"map-999\"},\"222\":{\"memo\":\"map-222\"}}"
          + "}";

    ObjectMapper mapper = new ObjectMapper();
    HogeBean obj = mapper.readValue(text, HogeBean.class);
  }
}

インタフェースクラスをインスタンス化しようとするので、例外がスローされる。

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: 
 Can not construct instance of DeserTest$Child, problem: abstract types either 
 need to be mapped to concrete types, have custom deserializer,
 or be instantiated with additional type information

このようなときは、Java Bean がインターフェースと実装に分離している場合にあるように、@JsonTypeInfoアノテーションを使って対応できる。

けれど、ソースに手を入れてアノテーションを設定することができないこともある。@JsonTypeInfoアノテーションを使わずに、Childインタフェースの実装クラスはChildImplクラスであることをObjectMapperに伝えるにはSimpleModule#addAbstractTypeMapping()を使うといいようだ。

import com.fasterxml.jackson.databind.module.SimpleModule;
...
    SimpleModule module = new SimpleModule();
    module.addAbstractTypeMapping(Child.class, ChildImpl.class);
    mapper.registerModule(module);

    HogeBean obj = mapper.readValue(text, HogeBean.class);
    System.out.println(obj.getMap());
    System.out.println(obj.getMap().getClass());

結果
{999=DeserTest\$ChildImpl@35af4d51, 222=DeserTest$ChildImpl@7fd1c60}
class java.util.LinkedHashMap

Mapインタフェースの実装をHashMapクラスに設定すると、当然そうなる。

    SimpleModule module = new SimpleModule();
    module.addAbstractTypeMapping(Child.class, ChildImpl.class);
    module.addAbstractTypeMapping(Map.class, HashMap.class);
    mapper.registerModule(module);
    ...

結果
{222=DeserTest\$ChildImpl@1f78a4e8, 999=DeserTest$ChildImpl@29928b7c}
class java.util.HashMap

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4