Skip to content

Instantly share code, notes, and snippets.

@wtokuno
Created March 7, 2012 21:07
Show Gist options
  • Save wtokuno/1996260 to your computer and use it in GitHub Desktop.
Save wtokuno/1996260 to your computer and use it in GitHub Desktop.
Jackson - JSON Processor

下書き

Jacksonとは

JacsonはJSON(JavaScript Object Notation)と呼ばれるデータ記述言語を扱うためのJavaライブラリ。

ウェブアプリケーション開発ではJSONが必須の技術になってきている。 JavaでJSONを扱うためのAPIがJava EE 7に含まれる予定。JSR 343として仕様策定中。

JSONとは

JSONはJavaScriptのリテラル表記をベースにしたデータ記述言語。 クライアントのウェブブラウザなどで利用できるJavaScriptで容易に扱えるから、Webサーバとのデータ交換フォーマットとして広く利用されている。

JSONの仕様

RFC 4627で仕様が規定されている。 またjson.orgでもJSONの仕様を確認することができる。 json.orgには日本語のページも用意されているので英語が苦手な方はこちらを参照。

JSONの概要

JSONには6つのデータ型がある。

  • オブジェクト
  • 配列
  • 文字列
  • 数値
  • 真偽値
  • null値

オブジェクト

オブジェクトは名前と値のペアを集めたデータ構造。

{}の間に名前と値のペアをカンマ区切りで表記する。 名前と値のペアは:で区切って表記する。 名前には文字列の表記を使う。 値には任意のJSONのデータを記述できる。 スカラー値だけでなく、データ構造も記述できるので、オブジェクトや配列がネストした複雑なデータを表現することができる。

オブジェクトの表記例

{}                                      // 空オブジェクト
{"val":null}                            // nullを値に持つ
{"val1":false, "val2":true}             // 真偽値を値に持つ
{"val1":123, "val2":12.3, "val3":12E3}  // 数値を値に持つ
{"val1":null, "val2":false, "val3":123} // 値の型が一様でなくてもよい
// 値に配列やオブジェクトを持つこともできる
{
  "extensions": ["java", "js"],
  "java":{"name":"Java", "typing":"static"},
  "js":{"name":"JavaScript", "typing":"dynamic"}
}
{"":"empty"}                            // 名前の部分は空文字列でもよい

間違ったオブジェクト表記例

{val:"wrong name"}                      // 名前が二重引用符で囲まれていない
{'val':"wrong name"}                    // 名前が単引用符で囲まれている
{123:"wrong name"}                      // 名前が文字列でない
{"val1":"Java", "val2":"JavaScript",}   // 余計なカンマ
{"val1":"Java", , "val2":"JavaScript"}  // 連続する2つのカンマ

名前と値のペアの表記順序は通常は意味を持たない。 名前と値のペアの表記順序が異なるオブジェクトであっても、同じ名前と値のペアのセットが表記されているなら、それらは意味的に同じ値を持つオブジェクトである。

配列

配列は順序付きで値を集めたデータ構造。

[]の間に要素となる値をカンマ区切りで表記する。 要素には任意のJSONのデータを記述できる。 スカラー値だけでなく、(以下オブジェクトと同じ)

配列の表記例
空配列
値にスカラー
要素のデータ型が一様でない
ネストした複雑なデータ構造
誤った配列の表記例
余計なカンマ
最後の要素の後ろに余計なカンマがある
要素の間に余計なカンマがある(2つ以上のカンマがある)

配列の要素は通常は順序を持つデータ構造として表現される。 表記されている要素の順序がそのまま配列のデータ構造での順序となる。

文字列

文字列は文字の並びを二重引用符"で囲んで表記する。 二重引用符、バックスラッシュ、制御文字(U+0000から`U+001F)を文字列の中に含めるにはエスケープする必要がある。

エスケープの表記方法は2種類ある。

一つはよく使う文字をバックスラッシュと特定のASCII文字の2文字で表記する方法。 この方法で表記できるエスケープは次の通り。

  • \": 二重引用符(", U+0022)
  • \\: バックスラッシュ(\, U+005C)
  • \/: スラッシュ(/, U+002F)
  • \b: バックスペース(U+0008)
  • \f: フォームフィード(U+000C)
  • \n: 改行、ラインフィード(U+000A)
  • \r: 復帰、キャリッジリターン(U+000D)
  • \t: 水平タブ(U+0009)

もう一つはユニコードのコードポイントを表記する方法。 エスケープ対象の文字が基本多言語面(BMP, U+0000からU+FFFF)の範囲であるなら、バックスラッシュ\uに続けて4桁の16進数でコードポイントを表記することでエスケープする。 基本多言語面の範囲外の拡張文字はUTF-16のサロゲートペアを組み合わせて表現する。

文字列の表記例
空文字列
ASCIIの範囲の文字列
ASCII範囲外の文字列
エスケープの例
2文字エスケープ
コードポイントのエスケープ
サロゲートペア
誤った文字列の表記例
単引用符で囲む

数値

数値は符号部、整数部、小数部、指数部をこの順序で組み合わせて表記する。

符号部マイナス記号-で表記する。プラス符号+を使うことはできず、正の値の場合には符号部を省略する。

整数部は10進数の数字の列。整数部の値が0である場合を除いて、0で始まる表記はできない。 整数部は省略できない。

小数部も10進数の数字の列。小数部は余計な0が後ろにあってもよい。整数部と小数部は小数点.で区切る。 小数部は省略できる。

指数部は符号と10進数の数値の列で表記する。 指数部の符号には-符号だけでなく+符号も使える。指数部の10進数の数値の列は余計な0で始まっていてもよい。 整数部や小数部と、指数部はeEのどちらか区切る。指数部は省略できる。

数値の表記例
ゼロ
整数
マイナス
小数
指数
小数と指数の組み合わせ
誤った数値の表記例
+符号をつける
整数部を省略

真偽値

真偽値はtruefalseと表記する。すべて小文字でなければならない。

真偽値の表記例
true
false
誤った表記例
大文字

null値

null値は意味のある値がないことを示す。null値はnullと表記する。すべて小文字でなければならない。

null値の表記例
null
誤った表記例
大文字

JSONとJavaScriptの表記の違い

JSONはJavaScriptのサブセットとして定義されているので、JavaScriptのリテラル表記を知っていればJSONを記述するのに困ることは少ない。とはいえJSONとJavaScriptのリテラル表記にはいくらか異なる点がある。JSONとJavaScriptの違いで引っかかりやすい点を挙げて説明する。

数値の前にゼロをつけてはならない

8進数や16進数の表記はできない

小数点からの表記はできない(整数部を省略できない)

オブジェクトの要素の名前は必ず文字列

オブジェクトや配列で余計なカンマを置くことはできない

文字列表記には二重引用符しか使えない(単引用符は使えない)

文字列のエスケープの違い

規定されていないエスケープ文字は使えない

JSONにない定数

  • undefined
  • NaN
  • Infinity

空文字列もオブジェクトの名前として使える

(これはJSONとJavaScriptとの違いではないかも)

JavaScriptにおけるJSONのパース

eval('(' + jsonText + ')'といったコードでJSONをパースすることができるものの、これはセキュリティ上の問題があるので避けるべき。 この方法でパースすると、JavaScriptのコードとして評価されるので、任意のJavaScriptのコードを実行してしまう脆弱性となる可能性がある。

最近のブラウザであれば、JSON.parse()メソッドとJSON.stringify()メソッドが追加されており、これを使って安全にJSONを扱うことができる。古いブラウザでは、これらのメソッドはサポートされていないが、json2.jsを使うことで、古いブラウザでもこれらのメソッドを使えるようになる。

<script src="json2.js"></script>
<script type="text/javascript">
  var jsonText = '["key1":"val1", "key2":"val2", "key3":"val3"]';
  var obj = JSON.parse(jsonText);
  var data = obj.key1;
</script>

Jacksonの使い方

jacksonのモジュール構成

Jacksonには次のようなモジュールがある。

  • Core: ストリーミングAPI(JSONパーサ、JSONジェネレータ)のインターフェースと実装など
  • Mapper(Optional): データバインディング機能など
    • ツリーモデルのためのTreeMapperJsonNode #TreeMapperなんてあったっけ?
    • JSONからJavaオブジェクトを生成したり、JavaオブジェクトをJSONとして書きだしたりするためのObjectMapper
  • JAX-RS(Optional): JerseyなどのJAX-RS実装でJacksonをお手軽に使える。
    • JAX-RSモジュールを使うにはMapperモジュールが必要(MapperモジュールがさらにCoreモジュールを必要とする)。
  • XC(Xml Compatibility)(Optional): Jacksonのアノテーションの代わりにJAXBアノテーションを使える。
  • MrBean(Materialized Bean)(Optional): インターフェースや抽象クラスから実装クラスを生成する機能など
    • MrBeanモジュールを使うにはMapperモジュールやCoreモジュールが必要。
  • Smile(Optional): SmileというJSONと互換なバイナリ形式のサポートなど
    • Smileモジュールを使うにはCoreモジュールが必要。

そのほかにも次のようなjarがある。

  • jackson-all: Jacksonのモジュールをすべてを含むjar。モジュールに分割されたjarが面倒なときに使う。(使うJacksonのバージョンを上げるときに分割されたモジュールのそれぞれのバージョンを同じに揃えるのが面倒とか?)あるいは、必要とする特定のクラスだけを含む、独自のサブセットのjarを作成するための元として使う。
  • jackson-mini: Coreモジュールからライセンスファイルやアノテーションクラスを取り除くなどして、jarのサイズを40%削減したもの

インストール

jarをダウンロードしてきて、クラスパスの通っているところににjarを置く。

mavenから使う方法

pom.xmlに次のような記述を追加する。

<repositories>
 <repository>
  <id>codehaus-snapshots</id>
  <url>http://snapshots.repository.codehaus.org</url>
 </repository>
</repositories>

<dependencies>
 <dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-mapper-asl</artifactId>
  <version>1.9.5</version>
 </dependency>
</dependencies>

Jacksonのライセンス

デュアルライセンス。AL 2.0とLGPL 2.1。

コンセプト

Jacksonの処理モデル

  • データバインディング
    • フルデータバインディング
    • シンプルデータバインディング
  • ツリーモデル
  • ストリーミングAPI

データバインディング

JSONとJavaオブジェクトとのバインディグ。 Jacksonの処理モデルの中で最も直感的で使いやすい処理モデルであろう。

データバインディング機能を使うにはMapperモジュール(と依存するCoreモジュール)が必要

シリアライズ

JavaオブジェクトをJSONテキストに変換することをここではシリアライズと呼ぶことにする。

ObjectMapperオブジェクトのwriteValue()メソッド(またはwriteValueAsBytes()メソッドかwriteValueAsString()メソッド)を使う。

Object obj = ...; // シリアライズするオブジェクト
OutputStream outputStream = ...; // シリアライズしたJSONを出力するストリーム

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(outputStream, obj);

writeValue()メソッドの第1引数はJSONテキストの出力先を指定する。writeValue()メソッドは第1引数の型でオーバーロードされており、JsonGeneratorFileOutputStreamWriterを指定できる。 writeValue()メソッドの第2引数にはシリアライズするJavaオブジェクトを指定する。

  • ObjectMapper#writeValue(JsonGenerator jgen, Object value): void
  • ObjectMapper#writeValue(File file, Object value): void
  • ObjectMapper#writeValue(OutputStream out, Object value): void
  • ObjectMapper#writeValue(Writer writer, Object value): void

writeValueAsBytes()メソッドとwriteValueAsString()メソッドは第1引数にシリアライズするJavaオブジェクトを指定する。 writeValueAsBytes()メソッドの返却値はシリアライズ結果をUTF-8エンコーディングしたbyte配列である。 writeValueAsString()メソッドの返却値はシリアライズ結果の文字列である。

  • ObjectMapper#writeValueAsBytes(Object value): byte[]
  • ObjectMapper#writeValueAsString(Object value): String
オブジェクトの型とシリアライズ規則

シリアライズは、シリアライズ対象のJavaオブジェクトの実際の型を基にシリアライズ結果のJSONテキストに出力する内容を決定する。

Map実装クラスはJSONオブジェクトに。 Java配列、Collection実装クラスはJSON配列に。(ただし、char配列を除く) char配列、CharSequence、String、StringBuilder、StringBufferはJSON文字列に。(ただし、WRITE_CHAR_ARRAYS_AS_JSON_ARRAYSが有効だとJSON配列に) Javaの数値型はJSON数値に。 Javaの真偽値はJSONの真偽値に。 Javaのnull値はJSONのnull値に。

その他のPOJOはJSONオブジェクトとして出力され、そのプロパティはゲッターやフィールドを元に出力される。

MapやCollectionの総称型の扱い 列挙型 日付型

enumはJSON文字列に。(ただしWRITE_ENUMS_USING_INDEXだとJSON整数に)

シリアライズ結果のJSONテキストに出力する内容は、シリアライズ対象のオブジェクトの実際の型を元にして決定される。しかし、POJOをシリアライズするとき、特定のプロパティはシリアライズ対象から除外したい場合がある。

次のようなメソッドを使って特定の型としてシリアライズし、そのサブクラスで宣言されているプロパティをシリアライズしないようにする。

  • ObjectMapper#writerWithType(Class<?> rootType): ObjectWriter
  • ObjectMapper#writerWithType(JavaType rootType): ObjectWriter
  • ObjectMapper#writerWithType(TypeReference<?> rootType): ObjectWriter

ObjectWriterオブジェクトのwithType()メソッドを使っても同様のことができる。

SerializationConfig.Feature.USE_STATIC_TYPINGを有効にすることで、POJOやMapなどのJSONオブジェクトにシリアライズされるJavaオブジェクトのプロパティやJava配列やCollectionなどのJSON配列にシリアライズされるJavaオブジェクトの要素をシリアライズするとき、プロパティや要素の実際の型ではなく、プロパティとして宣言されている型や要素として宣言されている型のプロパティのみをシリアライズすることができる。

プロパティの自動検出を制御したい場合: @AutoDetect,VisibilityChecker,AUTO_DETECT_GETTERS,AUTO_DETECT_IS_GETTERS,AUTODETECT_FIELDS

任意のプロパティを無視したい場合: @JsonIgnore プロパティのグループ分けで無視したい場合: View 無視するプロパティを動的に制御したい場合: Filter

フィールドの場合、transient修飾子をつけることでも無視されるが、transient修飾子の本来の目的に留意して付けるかどうかを決めるように。

デシリアライズ

JSONテキストからJavaオブジェクトを生成することをここではデシリアライズと呼ぶことにする。

ObjectMapperオブジェクトのreadValue()メソッドを使う。

InputStream inputStream = ...; // デシリアライズするJSONテキストの入力ストリーム

ObjectMapper objectMapper = new ObjectMapper();
<<デシリアライズ結果型>> result = objectMapper.readValue(inputStream, <<デシリアライズ結果型>>.class);
デシリアライズ先に指定できる型

ここで、デシリアライズ先の型とは次の通りである。

  • ObjectMapper#readValue()メソッドの第2引数に渡す型
  • JSONオブジェクトのプロパティがデシリアライズされるときの、デシリアライズ先のJavaの型のプロパティの型
  • JSON配列がデシリアライズされるときの、デシリアライズ先のJavaの型の要素の型

JSONの型に対して指定できるJavaの型は次のようになる。

  • JSONオブジェクト: Map, NavigableMap, SortedMapとMap実装クラス(抽象クラスはダメ)(実装クラスを指定した場合のインスタンス化の規則については要調査)(String配列を指定すると、なんか変な規則でデシリアライズできたような)

  • JSON配列: Java配列, Collection, List, Set, SortedSet, Queue, DequeとCollectionの実装クラス(抽象クラスはダメ) (char配列はダメだったような) (Java配列のときは、JSON配列の要素が、Java配列の要素の型に変換できる必要がある)

  • JSON文字列: char配列、CharSequence、String、StringBuilder、StringBuffer(JSON文字列が一文字の場合はcharでもいけたような)(Character配列はダメだったような) (enum型もOK) (日付時刻型(java.util.Date, java.util.Calendar)もOK。ただしjava.sql.Dateは注意が必要)(Joda TimeとかいうOSSの日付時刻関連のライブラリも対応しているらしい)

    • char[], CharSequence, String, StringBuilder, StringBuffer
    • boolean, Boolean: 文字列が"true"か"false"であればデシリアライズできる。大文字小文字は厳密に一致していなければならない。それ以外ではデシリアライズに失敗する。
    • char, Character
    • byte, short, int, long, Byte, Short, Integer, Long, BigInteger,
    • float, double, Float, Double, BigDecimal
    • Number
  • JSON数値: JSONの数値は整数と実数に分けられる。整数は小数点や指数表記のいずれも使われていない数値、実数は小数点と指数表記のいずれか、または両方が使われている数値のこと。ここでは1.0のように、値自体が整数であっても、表記に小数点や指数表記が使われているなら実数と呼ぶ。

    • JSON整数(小数点も、指数表記もないJSON数値):
      • boolean, Boolean: 0のときはfalse,それ以外のときはtrueになる。
      • char, Character: JSON整数に対応するコードポイントの文字になる。
      • 数値型(byte, short, int, long, float, double, Byte, Short, Integer, Long, Float, Double, BigInteger): char, byte, short, intとそのラッパー型であるCharacter, Byte, Short, Integerの場合は、その型で表現できる範囲に収まらない値からデシリアライズしようとすると、デシリアライズに失敗する。longとそのラッパー型であるLongの場合は、デシリアライズは失敗しないが、デシリアライズ結果の値は不正確な値になる。
      • BigDecimal: 精度はJSON整数の桁数、スケールは0
      • Number(シンプルデータバインディング)
      • Object(シンプルデータバインディング)
      • String, CharSequence: 内容が数値のString(char[]にはデシリアライズできない。)
      • StringBuilder, StringBuffer: 一応指定できるが、デシリアライズ結果は何もappendされていないStringBuilderおよびStringBufferとなる。
      • 列挙型: 指定された列挙型に、JSON整数と対応するインデックスの列挙値があれば、その値にデシリアライズされる。なければデシリアライズが失敗する。(FAIL_ON_NUMBERS_FOR_ENUMSを有効にすることで、列挙型が指定されたときに、デシリアライズに失敗するようにできる)
    • JSON実数(小数点か、指数表記のいずれか、または両方があるJSON数値):
      • 整数型(byte, short, int, long, Byte, Short, Integer, Long, BigInteger): 小数点以下は切り捨て
      • 実数型(float, double, Float, Double)
      • BigDecimal: 精度は整数部と小数部の桁数の合計(ただし、整数部が0で有る場合は桁数を0とする)、スケールは小数点以下の桁数-指数部の値(小数点表記で無い場合は、小数点以下の桁数を0として計算する。指数表記でない場合は、指数部の値を0として計算する)
      • Number(シンプルデータバインディング)
      • Object(シンプルデータバインディング)
      • String, CharSequence: 内容が数値のString(char[]StringBuilderStringBufferにはデシリアライズできない。)結果の文字列は、JSON数値として書かれていた表記と一字一句同じ文字列になる。例えば、12.3123E-1123e-004とではデシリアライズ結果はそれぞれ違う文字列になる。
      • char, Character, 列挙型などは指定できない
  • JSON真偽値:

    • boolean, Boolean
    • Object(シンプルデータバインディング)
    • StringCharSequenceを指定するとStringにデシリアライズされ、falseの場合は"false"に、trueの場合は"true"になる。(char配列、StringBuilderStringBufferにはデシリアライズできない。)
  • JSON null値:

    • 参照型: nullにデシリアライズされる。
    • プリミティブ型: デフォルト値にデシリアライズされる。(FAIL_ON_NULL_FOR_PRIMITIVESを有効にするとエラーになる)
プリミティブ型とJSONのnull値

デシリアライズ先がプリミティブ型で、デシリアライズ元のJSONの値がnull値であるとき、デシリアライズ結果の値はデフォルト値になる(ちなみにこのデフォルト値というのは、Javaの言語仕様で明確に定義されている概念で、クラス変数やインスタンス変数、配列の要素の値を指定しなかった場合の値はこのデフォルト値に初期化される)。プリミティブ型とデフォルト値の対応を以下に示す。

プリミティブ型 デフォルト値
boolean false
char '\u0000'
byte (byte) 0
short (short) 0
int 0
long 0L
float 0.0f
double 0.0d

DeserializationConfig.Feature.FAIL_ON_NULL_FOR_PRIMITIVESを有効にすることで、デフォルト値にデシリアライズされるのではなく、デシリアライズが失敗するように変更できる。

ただしプリミティブ型を要素型とする配列の場合には、FAIL_ON_NULL_FOR_PRIMITIVESを有効にしてもデシリアライズに失敗するようにはならないので注意すること。

また、char配列型では、要素の値がnull値かどうかに関係なく、そもそもJSON配列からデシリアライズできないので、FAIL_ON_NULL_FOR_PRIMITIVESの有効・無効に関係なく、必ずデシリアライズに失敗する。

抽象クラスやインターフェース

AbstractTypeResolver, MrBean

フルデータバインディング

JavaBeanとのバインディング

ゲッター、セッター、フィールド、クリエーターの自動検出

自動検出の可視性 SerializationConfig.Feature.AUTO_DETECT_GETTERS SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS SerializationConfig.Feature.AUTO_DETECT_FIELDS DeserializationConfig.Feature.AUTO_DETECT_SETTERS DeserializationConfig.Feature.AUTO_DETECT_CREATORS DeserializationConfig.Feature.AUTO_DETECT_FIELDS @AutoDetect VisibilityChecker

PropertyNamingStrategy

ゲッター、セッターからJacksonが決定するプロパティ名と、Java Beansの仕様の違いの問題

シンプルデータバインディング

Javaの基本的なデータ構造へのバインディング

JSONの型 Javaの型
オブジェクト LinkedHashMap<String, Object>
配列 ArrayList<Object>
文字列 String
数値(整数) Integer, Long, BigInteger
数値(実数) Double
真偽値 Boolean
null値 null

JSONのオブジェクトはHashMapではなく、LinkedHashMapにシリアライズされるので、JSONテキスト上で出現したプロパティの順番が保存されている。(とはいえJSONのオブジェクトは、プロパティの順番に意味を持たないのが想定されるため、この順番に依存した処理はすべきではないだろう)

整数は小数点や指数表記のない数値、実数は小数点か指数表記のいずれか、または両方がある数値のこと。

JSONの数値(整数)に対応するJavaの型は、IntegerLongBigIntegerの中で、JSONの整数を表現できる最小の型が選ばれる。

DeserializationConfig.Feature.USE_BIG_INTEGER_FOR_INTSを有効にすることで常にBigInteger型にデシリアライズされるようにできる。

DeserializationConfig.Feature.USE_BIG_DECIMAL_FOR_FLOATSを有効にすることでDoubleではなくBigDecimalにデシリアライズされるようにできる。

日付時刻

java.util.Datejava.util.Calendarのシリアライズ/デシリアライズができる。

日付時刻型はエポックタイム(1970-01-01 UTCからのミリ秒単位の経過時刻)を表す整数にシリアライズされる。 WRITE_DATES_AS_TIMESTAMPSを無効にすることで、ISO-8601に準拠した形式にシリアライズできる。(例: "1970-01-01T00:00:00.000+0000")

RFC-1123、ISO-8601形式の文字列と、エポックタイムの整数をデシリアライズできる。

Joda TimeというOSSの日付時刻ライブラリのDateTimejava.util.Dateと同様にして処理する。

java.sql.Dateは使わないように。Jacksonはjava.sql.Dateをサポートしているが、このクラスの設計に部分的に起因して、タイムゾーンの取り扱いに問題があることが分かっている。このクラスを使うのは避けて、代わりに可能な限りjava.util.Datejava.util.Calendarを使うこと。それが難しいのなら、java.util.Calendarを使って明示的にタイムゾーンを指定して、変換する必要があるかもしれない。

java.text.DateFormatでシリアライズ/デシリアライズをカスタマイズできる。

objectMapper.setDateFormat(...); // 1.8以降。シリアライズとデシリアライズの両方をカスタマイズ
objectMapper.getSerializationConfig().setDateFormat(...); // 1.8以降は非推奨。シリアライズのみ
objectMapper.getDeserializationConfig().setDateFormat(...); // 1.8以降は非推奨。デシリアライズのみ

列挙型

総称型

Polymorphic Type Handling

View

Filter

シリアライズ/デシリアライズのカスタマイズ

ツリーモデル

JSONのデータ構造を表すJavaオブジェクトでJSONを扱う処理モデル。XMLのDOMに相当する。

ツリーモデルを使うにはMapperモジュール(と依存するCoreモジュール)が必要

JSONテキストからのツリーモデルの生成

JSONテキストからツリーモデルを生成するには、データバインディングと同様にObjectMapperオブジェクトのreadValue()メソッドを使う方法と、ツリーモデルで読み込む専用のreadTree()メソッドを使う方法がある。

readValue()メソッドを使う方法

ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readValue(src, JsonNode.class);

readTree()メソッドを使う方法

ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(src);

ツリーモデルへのアクセス

  • JsonNode#get(int index): JSON配列のindexにある要素を返す。indexは0オリジン。
    • indexが配列の範囲外(0未満か配列のサイズ以上)ならnull
    • ノードがArrayNode型でないならnull
    • JSONのnull値はNullNode型であり、Javaのnullではないので区別できる。
  • JsonNode#get(String property): JSONオブジェクトの名前がpropertyの値と一致するプロパティの値を返す。
    • 名前がpropertyの値と一致するプロパティがないならnull
    • ノードがObjectNode型でないならnull
    • JSONのnull値はNullNode型であり、Javaのnullではないので区別できる。
  • JsonNode#path(int index), JsonNode#path(String property): get(int)get(String)と似ているが、値が見つからない場合にJavaのnullを返すのではなく、MissingNodeを返す。
    • MissingNodeJsonNodeのメソッドをすべて実装しており、そこからノードを取得するメソッドは常にMissingNodeを返す。これにより複数のプロパティや配列のインデックスを経由して値を取得するときに、nullチェックを行わなくてよい。 #いわゆるNullオブジェクトパターン
  • JsonNode#with(String property): path(String)メソッドと似ているが、MissingNodeを返すのではなく、新しいObjectNodeを生成してノードに追加し、追加したObjectNodeを返す。 #with(int index)がないのはなぜだろう。
    • 複数の(存在するか分からない)プロパティを経由して、値を設定する際に、プロパティが存在するかどうかをチェックして、自分でObjectNodeを生成して、追加しなくてよい。

一からのツリーモデルの生成

ObjectNodeの生成にはObjectMapperオブジェクトのcreateObjectNode()メソッドを使う。

ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.createObjectNode();

TODO: ArrayNodeの生成 TODO: JSONオブジェクトやJSON配列の要素の値を変更するには、ObjectNodeやArrayNodeにキャストする必要がある。

ツリーモデルのJSONテキストへの変換

ツリーモデルのJSONへの変換は通常のJavaオブジェクトの場合と同様にObjectMapperオブジェクトのwriteValue()メソッドを使う。

JsonNode rootNode = ...;

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(dst, rootNode);

トークンのストリームへの変換

JsonParserの項を参照

ツリーモデルからJavaオブジェクトへの変換

ツリーモデルを一旦トークンのストリームに変換してから、Javaオブジェクトに変換することもできるが、ObjectMapperオブジェクトのtreeToValue()メソッドを使うこともできる。

JsonNode node = ...;
MyBean bean = objectMapper.treeToValue(node, MyBean.class);

ストリーミングAPI

JSONテキストをトークンのストリームとして扱う処理モデル。XMLのStAXに相当する。

ストリーミングAPIを使うにはCoreモジュールが必要。

Jacksonの処理モデルの中で最も効率的な処理モデル。メモリや処理速度の最小限のオーバーヘッド。 性能と引き換えに使い勝手はあまりよくない。

  • JSONを読み込む順序と同じ順番で処理しなければならない。オブジェクトや配列の要素にランダムアクセスするにはデータバインディングかツリーモデルを使う必要がある。
  • オブジェクトや配列などのデータ構造は自分で作らないといけない。

パーサー

JSONテキストから

File file = ...; // 入力となるJSONテキスト

JsonFactory jsonFactory = new JsonFactory(); // or MappingJsonFactory
JsonParser jp = jsonFactory.createJsonParser(file);

ツリーモデルから

JsonNode node = ...;
JsonParser jp = node.traverse();

または

ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = ...;
JsonParser jp = objectMapper.treeAsTokens(node);

TokenBufferから

TokenBuffer buffer = ...;
JsonNode root = objectMapper.readTree(buffer.asParser());

ジェネレーター

ファイルに書き出し

File file = ...; // 出力先となるファイル

JsonFactory jsonFactory = new JsonFactory(); // or MappingJsonFactory
JsonGenerator jg = jsonFactory.createJsonGenerator(file, JsonEncoding.UTF8);

TokenBufferはJsonGeneratorを継承している

TokenBuffer buffer = new TokenBuffer();

on/off設定項目

SerializationConfig.Feature DeserializationConfig.Feature JsonGenerator.Feature JsonParser.Feature

アノテーション

Mix-In

AnnotationIntrospector

JAXBアノテーション

XCモジュールを使うと、Jacksonのアノテーションの代わりにJAXBのアノテーションを使える。

Materialized Bean (MrBean)

MrBeanの機能を利用するにはMrBeanモジュール(と依存するMapperモジュールやCoreモジュール)が必要

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());

ObjectMapper mapper = new ObjectMapper();
// AbstractTypeMaterializerはAbstractTypeResolverを継承している
mapper.getDeserializationConfig().setAbstractTypeResolver(new AbstractTypeMaterializer());

その他のJavaによるJSONライブラリ

gson(google-json)、JSONICなど

TODO

JSR 343のウォッチ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment