書くに至った経緯
Gson, Moshi, Jackson - DEV Community
↑のタイトル見て、Moshiって何だ?ってなったので、調べた。
とりあえず記事の日本語訳
deepl使ったけど、文化の違いか、意味の分からんところは意訳か省いてる。
あと、不要だなって思った文章も省いて簡略化している。
Gson, Moshi, Jackson
JSON Parserは、レスポンスをデータオブジェクトにデシリアライズしたり、情報オブジェクトをリクエストにシリアライズするために使用されます。
Gson
最も広く使われているのは、GoogleのAndroid用JSON Parserパッケージ。
Gson Javaパッケージを使って、JavaオブジェクトをJSON表現に変換することができる。また、JSONの文字列を対応するJavaオブジェクトに変換するために使うこともできます。
- Gsonは、Googleが運営する特定の標準に準拠した標準ライブラリです。
- 効率的 このJava標準ライブラリの追加は、信頼性が高く、迅速で、効率的です。
- 最適化の強化がなされている。
- ジェネリックのサポート ジェネリックのサポートが充実しています。
- 大規模な継承階層や、複雑な内部クラスを持つオブジェクトをサポートします。
今後のGson
- 使いやすい - Gson APIは、よく使われるユースケースを簡略化するための高レベルなファサードを提供します。
- マッピングを作成する必要がない - Gson APIは、シリアライズされるオブジェクトのほとんどについて、デフォルトのマッピングを提供します。
- パフォーマンス - Gsonは、比較的高速で、メモリフットプリントが小さい。大規模なオブジェクト・グラフやシステムに適しています。
- クリーンなJSON - Gsonは、読みやすい、すっきりとコンパクトなJSONの結果を作成します。
- 依存性がない - Gsonライブラリは、JDK以外の他のライブラリを必要としません。-オープンソース - Gsonライブラリは、オープンソースであり、自由に利用することができます。
Jackson
Jacksonパーサーは、JSONPやGSONのような他の一般的なパーシングライブラリと比較して、優れたパーシング速度を持っています。 Jackson には、JSON ファイルを解析してカスタム Java オブジェクトにデシリアライズする Object Mapper クラスが組み込まれています。これは、JavaオブジェクトからJSONを生成することを容易にするものです。
- Jackson Core(Streaming API) - 低レベルのストリーミング API が定義されており、JSON 固有の実装が含まれています。
- Jackson Annotations - 標準のJacksonアノテーションが含まれています。
- Jackson Databind - ストリーミング・パッケージでのデータ・バインディング(およびオブジェクト・シリアライゼーション)のサポートを提供します。
- フィールドにアノテーションを付けて、特定の JSON キーに対応させることができる機能。Jackson を使用すると、Java オブジェクトのフィールドにアノテーションを付けて、特定の JSON ドキュメントのキーに対応させることができます。その結果、複雑な JSON ドキュメントでの作業が大幅に簡素化されました。たとえば、@JsonProperty("name") アノテーションを使用すると、JSON ドキュメント内の "name" キーにマッピングされた Java オブジェクト内のフィールドにアクセスすることができます。
- POJO(Plain Old Java Objects)とJAXBビーンズ(Java Architecture for XML Binding)です。Jacksonは、JAXBビーンズとPOJOの両方に対応しています。定型的なコードは必要なく、オブジェクトのシリアライズとデシリアライズが可能になりました。
- さまざまなモジュールが追加機能を提供します。Jacksonには、追加機能を提供するいくつかのモジュールが含まれています。これらのモジュールはすべて、XML、YAML、およびCSV形式をサポートしています。
Moshi
3つのパーサーのうち最も新しい。
Gsonのチームメンバーも関わっている。
GSONと比較すると、Kotlinを意識して書いたという点が光ります。ポリモーフィックアダプタやプライマリTypeアダプタの利用やテスト作成がかなりシンプルになりました。これによって、プロジェクトのコード品質をさらに高めることができるのです。
Moshiは、JacksonやGsonのようなライブラリよりも、パフォーマンスを犠牲にすることなく、より凝縮されたAPIを提供します。その結果、よりテストしやすいコードを設計することができ、アプリへの統合がよりシンプルになります。Android向けの開発など、状況によっては、より少ない依存性で済むことが重要な場合があります。
Moshiは任意のランダムなJava Beanを、他の種類と同じガイドラインに従って値が変換されたJSONオブジェクトに自動的に変換します。これは、Java BeanがJava Bean内で必要なだけ深くシリアライズされることを示します。 その後、Moshi-adaptersの依存関係のおかげで、特定の追加変換ルールにアクセスすることができます。
Enumsアダプタはもう少し堅牢で、JSONから不確かな値を読み込む際のフォールバック値を有効にします。日付の RFC-3339 形式をサポートしています。
検証
github
GitHub - suzaku-tec/JsonSample
一部抜粋するので、本ソースが見たい人は↑
テスト用JSON
{ "name": "sample json", "userInfos": [ { "age": 1, "name": "json 1" }, // ・・・・ { "age": 500, "name": "json 500" } ] }
とりあえず、普通の要素とリスト要素(500件)を持つものを生成。
本来なら、たぶん、深いネストのjsonを用意するのが正しい気がするが、テストデータを生成する気力が分かんかった。。。
計測
// Gson Gson gson = new Gson(); startTime = System.currentTimeMillis(); GsonCompany gsonCompany = gson.fromJson(json, GsonCompany.class); System.out.println("gson:" + gsonCompany); endTime = System.currentTimeMillis(); System.out.println("処理時間:" + (endTime - startTime) + " ms"); // Jackson ObjectMapper mapper = new ObjectMapper(); startTime = System.currentTimeMillis(); JacksonCompany jacksonCompany = mapper.readValue(json, JacksonCompany.class); System.out.println("jackson:" + jacksonCompany); endTime = System.currentTimeMillis(); System.out.println("処理時間:" + (endTime - startTime) + " ms"); // Moshi Moshi moshi = new Moshi.Builder().build(); JsonAdapter<MoshiCompany> jsonAdapter = moshi.adapter(MoshiCompany.class); startTime = System.currentTimeMillis(); MoshiCompany moshiCompany = jsonAdapter.fromJson(json); System.out.println("moshi:" + moshiCompany); endTime = System.currentTimeMillis(); System.out.println("処理時間:" + (endTime - startTime) + " ms");
各ライブラリを利用してjson→java objectにマッピングさせている。
必要なオブジェクト生成で差が出そうだったので、単純に変換させるところだけ計測
処理時間:22 ms 処理時間:70 ms 処理時間:14 ms
上から、gson/jackson/moshi
jacksonが時間かかってるようにみえるけど、パフォーマンス計測的に、あまり信用できる方法でやってないので、もっと検証に時間をかければ、より差分は見えてきそう。
どっちかというと、利用方法に着目していたので、今回は深掘りはあまりしない。
使ってみて思ったこと
gsonは、知ってたけど、使ったこと無かった。
比較的、シンプルに書けて柔軟性が高い感じた。
private変数に入れられるので、lombok使った環境とかにも導入しやすいのかと思った。
ただ、俺はlombokアンチ民なので、メリットを感じないが。
jackson/moshiは、private変数には入れられないけど、publicになっていれば入れられるので、gsonからjackson/moshiに移行する場合は、変数のアクセス範囲には注意する必要がると感じた。
jackson -> moshiは、オブジェクトのデータ構造の変更の必要がないので、比較的楽に移行できそうだと感じた。逆もしかり。
アノテーションで柔軟性を広げているので、移行するときは、そこに注意が必要そう。
余談だが、moshiを maven repositoryで探した際、moshiがなぜかscope=runtimeになっていて、実装しているときに"なぜ使えないんだ?"ってすごく迷った。
なぜruntimeになっているんだ?