メンバ名がハイフン(-)区切りの 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
アノテーションを使用してみる。
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 をデシリアライズする例は:
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
アノテーションを使って次のように実装することができる。
public interface Order {
int getOrderId();
}
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;
}
}
public interface Person {
String getName();
Collection<Order> getOrders();
}
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 を実際にデシリアライズする例は:
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)
アノテーションを指定してやると、次のようになる。
@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;
}
}
@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
のファクトリを作成してやればプログラムは完成だ。
public class PersonFactory {
public Person fromJson(String jsonText) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(jsonText, PersonImpl.class);
}
}
PersonFactory
クラスを使って、実際にデシリアライズする例は:
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());