Jacksonを使用した動的JSONオブジェクトのマッピング

1.はじめに

Jacksonで事前定義されたJSONデータ構造を操作するのは簡単です。ただし、プロパティが不明な動的JSONオブジェクトを処理する必要がある場合があります

この短いチュートリアルでは、動的JSONオブジェクトをJavaクラスにマッピングする複数の方法を紹介します。

注すべてのテストでは、我々は、フィールドがあるとしていることobjectMapper型のcom.fasterxml.jackson.databind.ObjectMapperを

2.JsonNodeの使用

Webショップで製品仕様を処理したいとします。すべての製品にはいくつかの共通の特性がありますが、製品のタイプに応じて他の特性もあります。

たとえば、携帯電話のディスプレイのアスペクト比を知りたいのですが、このプロパティは靴にはあまり意味がありません。

データ構造は次のようになります。

{ "name": "Pear yPhone 72", "category": "cellphone", "details": { "displayAspectRatio": "97:3", "audioConnector": "none" } }

動的プロパティをdetailsオブジェクトに格納します。

次のJavaクラスを使用して共通のプロパティをマップできます。

class Product { String name; String category; // standard getters and setters }

その上、詳細オブジェクトの適切な表現が必要です。たとえば、com.fasterxml.jackson.databind.JsonNodeは動的キーを処理できます

これを使用するには、Productクラスにフィールドとして追加する必要があります。

class Product { // common fields JsonNode details; // standard getters and setters }

最後に、それが機能することを確認します。

String json = ""; Product product = objectMapper.readValue(json, Product.class); assertThat(product.getName()).isEqualTo("Pear yPhone 72"); assertThat(product.getDetails().get("audioConnector").asText()).isEqualTo("none");

ただし、このソリューションには問題があります。JsonNodeフィールドがあるため、クラスはJacksonライブラリに依存しています。

3.マップの使用

詳細フィールドにjava.util.Mapを使用することで、この問題を解決できます。より正確には、Mapを使用する必要があります。

他のすべては同じままでいられます:

class Product { // common fields Map details; // standard getters and setters }

そして、テストでそれを検証できます。

String json = ""; Product product = objectMapper.readValue(json, Product.class); assertThat(product.getName()).isEqualTo("Pear yPhone 72"); assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

4. @ JsonAnySetterの使用

以前のソリューションは、オブジェクトに動的プロパティのみが含まれている場合に適しています。ただし、単一のJSONオブジェクトに固定プロパティと動的プロパティが混在している場合があります

たとえば、製品表現をフラット化する必要がある場合があります。

{ "name": "Pear yPhone 72", "category": "cellphone", "displayAspectRatio": "97:3", "audioConnector": "none" }

このような構造を動的オブジェクトとして扱うことができます。残念ながら、それは共通のプロパティを定義できないことを意味します—それらも動的に処理する必要があります。

または、@ JsonAnySetter使用して、追加の不明なプロパティを処理するためのメソッドをマークすることもできます。このようなメソッドは、プロパティの名前と値の2つの引数を受け入れる必要があります。

class Product { // common fields Map details = new LinkedHashMap(); @JsonAnySetter void setDetail(String key, Object value) { details.put(key, value); } // standard getters and setters }

NullPointerExceptionsを回避するために、detailsオブジェクトをインスタンス化する必要があることに注意してください。

動的プロパティをマップに保存するため、以前と同じように使用できます。

String json = ""; Product product = objectMapper.readValue(json, Product.class); assertThat(product.getName()).isEqualTo("Pear yPhone 72"); assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

5.カスタムデシリアライザーの作成

ほとんどの場合、これらのソリューションは問題なく機能します。ただし、場合によっては、さらに多くの制御が必要になります。たとえば、JSONオブジェクトに関する逆シリアル化情報をデータベースに保存できます。

カスタムデシリアライザーを使用して、これらの状況をターゲットにすることができます。これは複雑なトピックであるため、別の記事で取り上げ、Jacksonでのカスタム逆シリアル化の開始を開始します。

6.結論

この記事では、Jacksonを使用して動的JSONオブジェクトを処理する複数の方法について説明しました。

いつものように、例はGitHubで入手できます。