1. 序章

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

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

すべてのテストで、タイプcom.fasterxml.jackson.databind.ObjectMapperのフィールド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
}

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

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

class Product {

    // common fields

    JsonNode details;

    // standard getters and setters
}

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

String json = "<json object>";

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. マップを使用する

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

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

class Product {

    // common fields

    Map<String, Object> details;

    // standard getters and setters
}

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

String json = "<json object>";

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<String, Object> details = new LinkedHashMap<>();

    @JsonAnySetter
    void setDetail(String key, Object value) {
        details.put(key, value);
    }

    // standard getters and setters
}

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

動的プロパティをMapに格納するため、以前と同じように使用できます。

String json = "<json object>";

Product product = objectMapper.readValue(json, Product.class);

assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

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

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

カスタムデシリアライザーを使用して、これらの状況をターゲットにすることができます。 これはより複雑なトピックであるため、別の記事ジャクソンでのカスタムデシリアライズの開始で説明します。

6. 結論

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

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