下書き
JacsonはJSON(JavaScript Object Notation)と呼ばれるデータ記述言語を扱うためのJavaライブラリ。
ウェブアプリケーション開発ではJSONが必須の技術になってきている。 JavaでJSONを扱うためのAPIがJava EE 7に含まれる予定。JSR 343として仕様策定中。
JSONはJavaScriptのリテラル表記をベースにしたデータ記述言語。 クライアントのウェブブラウザなどで利用できるJavaScriptで容易に扱えるから、Webサーバとのデータ交換フォーマットとして広く利用されている。
RFC 4627で仕様が規定されている。 またjson.orgでもJSONの仕様を確認することができる。 json.orgには日本語のページも用意されているので英語が苦手な方はこちらを参照。
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で始まっていてもよい。
整数部や小数部と、指数部はe
かE
のどちらか区切る。指数部は省略できる。
数値の表記例
ゼロ
整数
マイナス
小数
指数
小数と指数の組み合わせ
誤った数値の表記例
+符号をつける
整数部を省略
真偽値はtrue
やfalse
と表記する。すべて小文字でなければならない。
真偽値の表記例
true
false
誤った表記例
大文字
null値は意味のある値がないことを示す。null値はnull
と表記する。すべて小文字でなければならない。
null値の表記例
null
誤った表記例
大文字
JSONはJavaScriptのサブセットとして定義されているので、JavaScriptのリテラル表記を知っていればJSONを記述するのに困ることは少ない。とはいえJSONとJavaScriptのリテラル表記にはいくらか異なる点がある。JSONとJavaScriptの違いで引っかかりやすい点を挙げて説明する。
undefined
NaN
Infinity
(これはJSONとJavaScriptとの違いではないかも)
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には次のようなモジュールがある。
- Core: ストリーミングAPI(JSONパーサ、JSONジェネレータ)のインターフェースと実装など
- Mapper(Optional): データバインディング機能など
- ツリーモデルのための
TreeMapper
やJsonNode
#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を置く。
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>
デュアルライセンス。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引数の型でオーバーロードされており、JsonGenerator
、File
、OutputStream
、Writer
を指定できる。
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整数の桁数、スケールは0Number
(シンプルデータバインディング)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[]
、StringBuilder
、StringBuffer
にはデシリアライズできない。)結果の文字列は、JSON数値として書かれていた表記と一字一句同じ文字列になる。例えば、12.3
と123E-1
と123e-004
とではデシリアライズ結果はそれぞれ違う文字列になる。char
,Character
, 列挙型などは指定できない
- 整数型(
- JSON整数(小数点も、指数表記もないJSON数値):
-
JSON真偽値:
boolean
,Boolean
Object
(シンプルデータバインディング)String
やCharSequence
を指定するとString
にデシリアライズされ、false
の場合は"false"
に、true
の場合は"true"
になる。(char
配列、StringBuilder
、StringBuffer
にはデシリアライズできない。)
-
JSON null値:
- 参照型:
null
にデシリアライズされる。 - プリミティブ型: デフォルト値にデシリアライズされる。(
FAIL_ON_NULL_FOR_PRIMITIVES
を有効にするとエラーになる)
- 参照型:
デシリアライズ先がプリミティブ型で、デシリアライズ元の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の型は、Integer
、Long
、BigInteger
の中で、JSONの整数を表現できる最小の型が選ばれる。
DeserializationConfig.Feature.USE_BIG_INTEGER_FOR_INTS
を有効にすることで常にBigInteger
型にデシリアライズされるようにできる。
DeserializationConfig.Feature.USE_BIG_DECIMAL_FOR_FLOATS
を有効にすることでDouble
ではなくBigDecimal
にデシリアライズされるようにできる。
java.util.Date
とjava.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の日付時刻ライブラリのDateTime
をjava.util.Date
と同様にして処理する。
java.sql.Date
は使わないように。Jacksonはjava.sql.Date
をサポートしているが、このクラスの設計に部分的に起因して、タイムゾーンの取り扱いに問題があることが分かっている。このクラスを使うのは避けて、代わりに可能な限りjava.util.Date
やjava.util.Calendar
を使うこと。それが難しいのなら、java.util.Calendar
を使って明示的にタイムゾーンを指定して、変換する必要があるかもしれない。
java.text.DateFormat
でシリアライズ/デシリアライズをカスタマイズできる。
objectMapper.setDateFormat(...); // 1.8以降。シリアライズとデシリアライズの両方をカスタマイズ
objectMapper.getSerializationConfig().setDateFormat(...); // 1.8以降は非推奨。シリアライズのみ
objectMapper.getDeserializationConfig().setDateFormat(...); // 1.8以降は非推奨。デシリアライズのみ
JSONのデータ構造を表すJavaオブジェクトでJSONを扱う処理モデル。XMLのDOMに相当する。
ツリーモデルを使うにはMapperモジュール(と依存するCoreモジュール)が必要
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
を返す。MissingNode
はJsonNode
のメソッドをすべて実装しており、そこからノードを取得するメソッドは常に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への変換は通常のJavaオブジェクトの場合と同様にObjectMapperオブジェクトのwriteValue()メソッドを使う。
JsonNode rootNode = ...;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(dst, rootNode);
JsonParserの項を参照
ツリーモデルを一旦トークンのストリームに変換してから、Javaオブジェクトに変換することもできるが、ObjectMapperオブジェクトのtreeToValue()メソッドを使うこともできる。
JsonNode node = ...;
MyBean bean = objectMapper.treeToValue(node, MyBean.class);
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();
SerializationConfig.Feature DeserializationConfig.Feature JsonGenerator.Feature JsonParser.Feature
XCモジュールを使うと、Jacksonのアノテーションの代わりにJAXBのアノテーションを使える。
MrBeanの機能を利用するにはMrBeanモジュール(と依存するMapperモジュールやCoreモジュール)が必要
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());
ObjectMapper mapper = new ObjectMapper();
// AbstractTypeMaterializerはAbstractTypeResolverを継承している
mapper.getDeserializationConfig().setAbstractTypeResolver(new AbstractTypeMaterializer());
gson(google-json)、JSONICなど
JSR 343のウォッチ