多分どこよりも早いJSON-B解説(Java EE 8・Java API for JSON Binding)

JSON-Bって何?

Java EE 8で追加される予定の、JavaオブジェクトとJSONの相互変換を行う機能です。例えば、下記のようなJAX-RSコードがあるとします。

public class Employee {
  private int id;
  private String name;
  private java.time.LocalDate joinedDate;
  // コンストラクタ、setter、getterは省略
}

@Path("employee")
public class EmployeeResource {
  @GET
  @Produces("application/json")
  public Response getEmployee() {
    Employee emp = new Employee(100, "Hoge", LocalDate.of(2015, 4, 1));
    return Response.ok(emp).build();
  }
}

で、http://localhost:8080/sample/api/employeeのようなURLにGETリクエストを送ると、下記のようなJSONが返ってきます。

{
  "id" : 100,
  "name" : "Hoge",
  "joinedDate" : "2015-04-01"
}

実は今まで、JAX-RS(およびJava EE)自体にはJavaオブジェクトをJSONに変換する機能を持っていませんでした。このJSON変換の部分は、APサーバーが内包している「JSONパーサー」と呼ばれる別ライブラリ(EclipseLink MOXy、Jackson、Jettisonなど)が行っています。Java EE 7で「JSON Processing (JSON-P)」という機能が追加されましたが、これはJSONを動的に読み書きするもので、前述のJSONパーサーのような、オブジェクトを一括でJSONに変換するものではありません。

そこで、JSONパーサーをJava EE標準に取り入れようということで、Java EE 8に追加される予定の機能が「JSON-B」です。2015年8月に、JSR 367のEarly Draft第1版が公開されました。下記のURLからダウンロード可能です。ダウンロードできるZIPには、クラスファイル・ソース・JavadocのJARが1つずつ、およびJSR文書のPDFが含まれています。

JSR-000367 Java API for JSON Binding Early Draft Review

ただし、JSRで決まっているのはほとんどインターフェイスアノテーション・例外なので、プログラムとして動かすためには「実装」が必要になります。しかしJSON-Bの参照実装(公式の実装)であるEclipseLink MOXyには、探した限りではまだJSON-B実装が含まれていないようです。そこで今回は、JSRやJavadocから分かる範囲で、JSON-Bの仕様を解説したいと思います。プログラムとしての作成・実行を確認できていないので、その点はご了承くださいm( )m

JavaオブジェクトとJSONの相互変換

変換の中心となるのは、javax.json.bind.Jsonbインターフェイスです。ただし、Jsonbインターフェイスについては、まだJSRには記載されていませんので、Javadocに載っていたサンプルコードからご紹介します。

Javaオブジェクト→JSONへの変換(マーシャリング)

JsonbインターフェイスのtoJson()メソッドを使います。

Employee emp = new Employee(100, "Hoge", LocalDate.of(2015, 4, 1));
// Jsonbオブジェクトの取得
Jsonb jsonb = JsonbBuilder.create();
// JavaオブジェクトをJSON文字列に変換する
String json = jsonb.toJson(emp);

toJson()メソッドオーバーロードされており、第2引数にOutputStreamを指定することも可能です。

jsonb.toJson(emp, new PrintWriter(System.out));

他にも、引数が違うtoJson()メソッドがいくつか存在します。

JSONJavaオブジェクトへの変換(アンマーシャリング)

JsonbインターフェイスのfromJson()メソッドを使います。

String json = "{\"id\":100,\"name\":\"Hoge\",\"joinedDate\":\"2015-04-01\"}";
Employee emp = jsonb.fromJson(json, Employee.class);

fromJson()メソッドオーバーロードされており、第1引数にInputStreamを指定することも可能です。

InputStream is = …;
Employee emp = jsonb.fromJson(is, Employee.class);

他にも、引数が違うfromJson()メソッドがいくつか存在します。

JAX-RSでの利用について

これは予測ですが、Java EE 8でJAX-RSを利用する際は、上記のようなtoJson() / fromJson()を使ったコードを書く機会は、そんなに多くないと思います。おそらく、JSON-Bを利用したMessageBodyWriter / MessageBodyReader(JSONパーサーを呼び出して、実際にJSON変換を行うJAX-RSのクラス)が、JAX-RS実装内(Jersey、RESTEasyなど)で組み込みで提供されるはずです。しかし、MessageBodyWriter / MessageBodyReaderを自分でカスタマイズしたい場合は、上記のようなコードを書く機会があるかもしれません。

対応しているデータ型

基本的なデータ型

  • java.lang.Character
  • java.lang.Byte
  • java.lang.Short
  • java.lang.Integer
  • java.lang.Long
  • java.lang.Float
  • java.lang.Double
  • java.lang.Boolean
  • 上記に対応するプリミティブ型
  • java.lang.String

日付

  • java.util.Date, Calendar, GregorianCalendar
  • java.util.TimeZone, SimpleTimeZone
  • java.time.*

コレクション

リスト・セット・マップのほぼ全て、および配列

JSON-P

  • javax.json.JsonObject
  • javax.json.JsonArray
  • javax.json.JsonStructure
  • javax.json.JsonValue
  • javax.json.JsonPointer
  • javax.json.JsonString
  • javax.json.JsonNumber

その他

マッピングのカスタマイズ

特定のフィールドをJSONに含めたくない

JSONに含めたくないフィールドに@javax.json.bind.annotation.JsonbTransientアノテーションを付加します。

public class Employee {
  // idはJSONに含まれない
  @JsonbTransient
  private int id;
  …
}

nullの場合もJSONに含めたい

デフォルトのルールでは、null値のフィールドはJSONに含まれません。nullの場合もJSONに含めたい場合は、@javax.json.bind.annotation.JsonbPropertyアノテーションnillable属性をtrueにします。

public class Employee {
  // nullでもJSONに出力される
  @JsonbProperty(nillable = true)
  private LocalDate joinedDate;
  …
}

フィールド名とJSONプロパティ名を変えたい

名前を変えたいフィールドに@javax.json.bind.annotation.JsonbPropertyアノテーションを付加し、value属性に任意の名前を指定します。

public class Employee {
  // JSONには「joined_date」という名前で出力される
  @JsonbProperty(value = "joined_date")
  private LocalDate joinedDate;
  …
}

その他のカスタマイズ事項

ネーミングルールを一括で決めるjavax.json.bind.config.PropertyOrderStrategy
  • IDENTITY
  • LOWER_CASE_WITH_DASHES
  • LOWER_CASE_WITH_UNDERSCORES
  • UPPER_CAMEL_CASE
  • UPPER_CAMEL_CASE_WITH_SPACES
  • CASE_INSENSITIVE

の6種類が指定可能です。

プロパティの順序を決めるjavax.json.bind.config.PropertyOrderStrategy
  • LEXICOGRAPHICAL(辞書順)
  • REVERSE(辞書順の逆)
  • ANY("the order of properties is not guaranteed to retain any order."とありますが、ちょっと意味が取れませんした・・・(^^; )

の3種類が指定可能です。

I-JSON (Internet JSON)サポート

I-JSONはあまり詳しくないので、詳細はこちらをお読みくださいm( )m

https://www.tbray.org/ongoing/When/201x/2015/03/23/i-json

可視性のカスタマイズ

ここで言う「可視性」とは、Javaのpublicとかprivateとかのことです。デフォルトでは、フィールドまたはgetterはpublicなもの以外は無視される(getterとフィールドが共にpublicの場合は、getterが優先されるようです)のですが、その設定を変えることができるのかな?ちょっとJSRとJavadocからは読み取れませんでした。

日付のフォーマット

@javax.json.bind.anntation.JsonbDateFormatアノテーションで指定します。属性でパターンとロケールの指定が可能です。

数値のフォーマット

@javax.json.bind.anntation.JsonbNumberFormatアノテーションで指定します。属性でパターンとロケールの指定が可能です。

バイナリデータ

javax.json.bind.config.BinaryDataStrategyクラスで指定します。以下の3つのエンコード方式が利用できます。

  • BYTE
  • BASE_64
  • BASE_64_URL
JSONの整形(Pretty Print)

デフォルトでは、多くのJSONパーサーは、JSONを下記のような形で生成します。

{"id":100,"name":"Hoge","joinedDate":"2015-04-01"}

これだと人間には読みづらいので、改行・空白・インデントを入れて読みやすくするのがPretty Printです。

{
  "id" : 100,
  "name" : "Hoge",
  "joinedDate" : "2015-04-01"
}

この機能はまだJSRには入っていませんが、Javadocには記載されています。javax.json.bind.JsonbConfigクラスを利用します。

JsonbConfig confing = new JsonbConfig();
// Pretty Print設定を有効化する
config.withFormatting(true);
Jsonb jsonb = JsonbBuilder.create(config);
// 整形されたJSONが生成される
String json = jsonb.toJson(…);

まとめ

今回の記事では、JSRおよびJavadocのうち主要と思われる部分のみ解説しました。全内容は解説していませんので、他の機能も知りたいという方は原典をお読みください。

JSR-000367 Java API for JSON Binding Early Draft Review

今までは、Java EE標準でないJSONパーサーやJAXBを使うしかなかったので、JSON-Bの追加は非常に嬉しいです。まだ仕様の策定中なので、今後さらに機能が追加されると思います。期待して待ちましょう!