ツリーモデルAPIでJSONデータを読み込む
前回は「Jackson Java JSON-processor」(以下、Jackson)のストリーミングAPIを利用して、JavaプログラムからJSON形式のデータにアクセスする方法を解説しました。今回はそれに引き続き、ツリーモデルのAPIを使う方法を紹介します。JacksonのツリーモデルAPIは、XMLのDOM APIに相当するもので、一度メモリ上に全てのデータを読み込んでオブジェクトのツリーを構築するため、先頭から順番にデータを読み込むストリーミングAPIに比べて柔軟なデータアクセスが可能です。
ツリーモデルの核になるのは、ブジェクトツリーのノードを表すJsonNodeクラスです。JsonNodeオブジェクトは、複数の子ノードと、それに紐付くフィールド名の情報を保持しています。子ノードを表すJsonNodeオブジェクトはget()メソッドで取得することができます。JsonNodeが配列のノードを表すものである場合には、get()メソッドの引数には取得したいノードのインデックスを整数で指定します。JsonNodeがオブジェクトを表すものである場合にはフィールド名を文字列で指定します。JsonNodeが文字列や数値などの値を持つものである場合には、getXxxxValue()というメソッドでその値を取得できます。文字列の値であればgetStringValue()メソッドです。なお、これらのノードの種類はorg.codehaus.jackson.nodeパッケージ以下のXxxxNodeクラスで区別されます。
ファイルかデータを読み込んでツリーを構築するにはObjectMapperクラスを利用します。まず、ObjectMapperのreadValue()メソッドを用いてルートノードのJsonNodeオブジェクトを取得しましょう。readValue()の第一引数には読込み元のFileオブジェクトや入力ストリームを、第二引数にはJsonNodeのClassオブジェクトを指定します。
今回は、次のようなJSONデータを読み込むケースを考えてみます。
このデータを読み込んで、その内容を表示するコードの例は次のようになります。
この例では、ルートノードと"name"オブジェクトには子ノードがいくつあっても走査できるようになっています。配列ノードに対するget()メソッドは、対象のノードが存在しない場合にはnullを返すので、これを終了条件にしています。オブジェクトノードからは、getFieldNames()メソッドでフィールド名のリストがIteratorオブジェクトとして取得できるため、すべての子ノードを走査する場合に利用できます。
このコードを実行すると、コンソールには次のように表示されて、データが読み込めていることが確認できます。
ツリーモデルAPIでJSONデータを書き出す
続いて、データの書き出しを行ってみましょう。先ほども書きましたが、ノードの種類は配列やオブジェクト、各型の値などによって、JsonNodeを継承する次のようなクラスで定義されています。このうち、BinaryNode以下は値を表すノードで、全てValueNodeのサブクラスとなっています。
- ArrayNode
- ObjectNode
- BinaryNode
- BooleanNode
- NumericNode
- POJONode
- TextNode
- NullNode
ArrayNodeにノードや値を追加するには、add()またはaddXxxx()メソッドを使用します。addXxxx()メソッドは新規にArrayNodeやObjectNodeを作成して追加するためのメソッドで、追加したノードオブジェクトを戻り値として返します。たとえば次のように記述した場合、rootNodeにはObjectNodeが新規に追加され、firstObjectにはそのObjectNodeが返されます。
ObjectNodeにノードや値を追加する場合には、putまたはputXxxx()メソッドを使用します。ArrayNodeの場合と同様に、putXxxx()メソッドでは新規にArrayNodeやObjectNodeを作成して追加するためのメソッドです。ただし、addXxxx()と異なり、引数にはフィールド名を指定する必要があります。たとえば次のように記述した場合、firstObjectには「name」というフィールド名を持ったObjectNodeが新規に追加され、nameObject1にはそのObjectNodeが返されます。
起点となるノードは、ObjectMapperのcreateArrayNode()メソッドやcreateObjectNode()メソッドを使って生成できます。
作成したJSONデータをファイルへ出力するにはObjectMapperのwrite()メソッドを使います。このメソッドにノードオブジェクトを渡すと、それ以下のノードのデータが指定されたファイルや出力ストリームに書き出されます。
以上を踏まえて、次のようなJSONデータを生成するプログラムを考えてみましょう。
ネストを含む複数のオブジェクトが配列に格納されている形のデータです。これは次のようなコードで生成できます。
Javaオブジェクトとのバインディングもサポート
Jacksonには、ストリーミングAPIとツリーモデルAPIの他に、JavaオブジェクトとのバインディングによってJSONデータの読み書きを行うAPIも用意されています。バインディングAPIは、MapやArrayList、StringといったJavaの既存の型に関連付けられるものと、任意のJavaオブジェクト(Java Beanオブジェクト)に関連付けられるものの2種類があります。
前者の場合、JSONのデータ型とJavaのクラスは次の表のように関連付けられます。
JSONの型 | Javaのクラス |
オブジェクト | LinkedHashMap<String,Object> |
配列 | ArrayList<Object> |
文字列 | String |
整数 | Integer, Long, BigInteger |
浮動小数点数 | Double(設定によってBigDecimalを使用することも可) |
true/false | Boolean |
null | null |
後者の場合、任意のJavaクラスのオブジェクトをJSONデータに変換することが可能です。たとえばUserクラスおよびNameクラスが次のように定義されているとします。
このとき、UserオブジェクトはObjectMapperクラスを利用してJSONデータとして出力することができます。出力方法はJsonNodeの場合と同様です。
このように、Jacksonでは複数の方法によるJSONデータの読み書きがサポートされているので、用途に応じて使い分けられるという点が大きなメリットになっています。