多分どこよりも早い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()メソッドがいくつか存在します。
JSON→Javaオブジェクトへの変換(アンマーシャリング)
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
日付
コレクション
リスト・セット・マップのほぼ全て、および配列
JSON-P
- javax.json.JsonObject
- javax.json.JsonArray
- javax.json.JsonStructure
- javax.json.JsonValue
- javax.json.JsonPointer
- javax.json.JsonString
- javax.json.JsonNumber
その他
- Enum
- java.math.BigInteger
- java.math.BigDecimal
- java.net.URL
- java.net.URI
- java.util.Optional
- java.util.OptionalInt
- java.util.OptionalLong
- java.util.OptionalDouble
マッピングのカスタマイズ
特定のフィールドを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の追加は非常に嬉しいです。まだ仕様の策定中なので、今後さらに機能が追加されると思います。期待して待ちましょう!