Jackson ObjectMapper Tips

# メンバ名がハイフン(-)区切りの JSON を Java Bean にデシリアライズする方法

メンバ名がハイフン(-)区切りの JSON を Jackson の ObjectMapper でデシリアラズしようとすると例外 `UnrecognizedPropertyException: Unrecognized field "xxx-xxx" (Class jp.co.ilu.abx2.result.json.Order), not marked as ignorable` が発生する。
この場合、`@JsonProperty` アノテーションを使用して、メンバ名を指定してやればよい。

注文を表す `Order` Bean に、`@JsonProperty` アノテーションを使用してみる。

```java:Order.java
public class Order {
  private int orderId;

  @JsonProperty("order-id")
  public int getOrderId() {
    return this.orderId;
  }

  @JsonProperty("order-id")
  public void setOrderId(int orderId) {
    this.orderId = orderId;
  }
}
```

JSON をデシリアライズする例は：

```java:例-1
ObjectMapper mapper = new ObjectMapper();
Order order = mapper.readValue("{ \"order-id\":480310 }", Order.class);
System.out.println(order.getOrderId());
```

# Java Bean がインターフェースと実装に分離している場合
実装クラスを `@JsonTypeInfo` アノテーションを使用して指定してやるとデシリアライズできるようになる。

先ほどの `Order` Bean を `Order` インターフェースとその実装である `OrderImpl` とに分離し、`Order` の一覧をもつ `Person` インターフェースとその実装である `PersonImpl` を、`@JsonTypeInfo` アノテーションを使って次のように実装することができる。

```java:Order.java
public interface Order {
  int getOrderId();
}
```

```java:OrderImpl.java
class OrderImpl implements Order{
  private int orderId;

  @JsonProperty("order-id")
  public int getOrderId() {
    return this.orderId;
  }

  @JsonProperty("order-id")
  public void setOrderId(int orderId) {
    this.orderId = orderId;
  }
}
```

```java:Person.java
public interface Person {
  String getName();

  Collection<Order> getOrders();
}
```

```java:PersonImpl
class PersonImpl implements Person {
  private String name;
  private Collection<Order> orders;

  public String getName() {
    return this.name;
  }

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

  @JsonTypeInfo(use=JsonTypeInfo.Id.NONE, defaultImpl=OrderImpl.class)
  public Collection<Order> getOrders() {
    return this.orders;
  }

  @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, defaultImpl=OrderImpl.class)
  public void setOrders(Collection<Order> orders) {
    this.orders = orders;
  }
}
```

Order と Person を実際にデシリアライズする例は：

```java:例-2
String jsonText = "{\"name\":\"sunny4381\",\"orders\":[{ \"order-id\":480310 },{ \"order-id\":480320 }]}";
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(jsonText, PersonImpl.class);
System.out.println(person.getName());
```

`PersonImpl.getOrders()` メソッドの `@JsonTypeInfo` アノテーションには、`use=JsonTypeInfo.Id.NONE` を指定している。
これを指定しないと、`PersonImpl` クラスのインスタンスを ObjectMapper を使用して JSON にシリアライズする際に、余計なメンバ`@type` が出力される。
`use=JsonTypeInfo.Id.NONE` を指定することで、シリアライズした際に、余計なメンバーが出力されることを抑制している。

# JSON に余分なメンバが存在していてもシリアライズに成功するようにする方法

Jackson ObjectMapper の既定の動作では、JSON に余分なメンバが存在している場合、例外 `UnrecognizedPropertyException: Unrecognized field "extra-member" (Class jp.co.ilu.abx2.result.json.Order), not marked as ignorable` が発生する。
これを抑制するには、`@JsonIgnoreProperties(ignoreUnknown=true)` アノテーションをクラスに指定してやる。

先ほどの `OrderImpl` クラスと `PersonImpl` クラスに `@JsonIgnoreProperties(ignoreUnknown=true)` アノテーションを指定してやると、次のようになる。

```java:OrderImpl.java
@JsonIgnoreProperties(ignoreUnknown=true)
class OrderImpl implements Order{
  private int orderId;

  @JsonProperty("order-id")
  public int getOrderId() {
    return this.orderId;
  }

  @JsonProperty("order-id")
  public void setOrderId(int orderId) {
    this.orderId = orderId;
  }
}
```

```java:PersonImpl
@JsonIgnoreProperties(ignoreUnknown=true)
class PersonImpl implements Person {
  private String name;
  private Collection<Order> orders;

  public String getName() {
    return this.name;
  }

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

  @JsonTypeInfo(use=JsonTypeInfo.Id.NONE, defaultImpl=OrderImpl.class)
  public Collection<Order> getOrders() {
    return this.orders;
  }

  @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, defaultImpl=OrderImpl.class)
  public void setOrders(Collection<Order> orders) {
    this.orders = orders;
  }
}
```

そして、最後に `Person` のファクトリを作成してやればプログラムは完成だ。

```java:PersonFactory.java
public class PersonFactory {
  public Person fromJson(String jsonText) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    return mapper.readValue(jsonText, PersonImpl.class);
  }
}
```


`PersonFactory` クラスを使って、実際にデシリアライズする例は：

```java:例-3
String jsonText = "{\"name\":\"sunny4381\",\"orders\":[{ \"order-id\":480310 },{ \"order-id\":480320 }],\"extra-member\":\"mem\"}";
PersonFactory factory = new PersonFactory();
Person person = factory.fromJson(jsonText);
System.out.println(person.getName());
```