How to create REST client in Java (3)

REST

We will continue to create REST client in Java.
In this article, Jersey client receives JSON format data and maps them to Java objects.

はじめに

【第二回】では GETリクエストで取得したリソース をJavaオブジェクトにマッピングするための方法について書きましたが、前回の環境ではリソースがXMLの場合しか正しく動作しません。

今回はこれを JSON の場合でも Javaオブジェクトにマッピングできるようにします。

当初コードの変更が必要だと思っていたのですが、再度確認したところ前回のコードでもJSON→Javaオブジェクトへマッピングできました。
先日コードを書いていた際には例外が発生してしまったため、勘違いしたようです。

どのような例外が発生して、どういった対応を行ったかについても記述します。

JSON を Java オブジェクトにマッピングする

JSON を Java オブジェクトにマッピングする場合、第一回で示したライブラリ以外に以下ライブラリが追加で必要になります。

  • jersey-json

第一回と同様に、maven を利用している場合、以下の dependency 設定を行います

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>1.18.1</version>
        </dependency>

上記ライブラリ追加後に、リビルド ( コードの再コンパイル )、再実行を行う事で、JSONからJavaオブジェクトのマッピングが行えるようになります。

テストクラスのコードは以下になります。

RestTest.java

package jp.co.agilegroup.rest;

import javax.ws.rs.core.MediaType;

import jp.co.agilegroup.magnolia.rest.dto.Node;

public class RestTest {
    public static void main(String[] args) {
        RestClient client = new RestClient("superuser","superuser");
        String uri = "http://localhost:8080/magnoliaAuthor/.rest/nodes/v1/website/demo-project/about";

        Node node = client.getEntity(uri, Node.class, MediaType.APPLICATION_JSON_TYPE);
        System.out.println("identifier:" + node.identifier);
        System.out.println("name:" + node.name);
        System.out.println("type:" + node.type);
        System.out.println("path:" + node.path);
    }
}

前回発生した問題

前回 JSON から Javaオブジェクトへのマッピングを試していた際に、以下の例外が発生してしまい、上述したようにコードの変更も行っていました。
今回の記事を書くにあたって再検証した結果、このコード変更は行う必要はない事が判りましたが、一応これに関しても書いておきます。

発生した例外

発生した例外は以下

A message body reader for Java class jp.co.agilegroup.magnolia.rest.dto.Node, and Java type class jp.co.agilegroup.magnolia.rest.dto.Node, and MIME media type application/json; charset=UTF-8 was not found

原因

原因ですが、おそらく、jersey-json を追加してリビルドしたつもりが、正しくリビルドされていない状態でコードを実行したためと思われます。
で、これを解決すべく RestClient.java の getClient メソッドを以下のように変更しました。

参考 : Chapter 5. JSON Support : Jersey 1.18 User Guide

    private Client getClient() {
        ClientConfig config = new DefaultClientConfig();
        config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
        Client client = Client.create(config);
        client.addFilter(new HTTPBasicAuthFilter(account, password));
        return client;
    }

おそらく、このコード変更後にきちんとリビルドが走ったのでしょう。
上記変更後のコードでテストを再実行すると、以下の例外がスローされる事になります。

com.sun.jersey.api.client.ClientHandlerException: org.codehaus.jackson.map.exc.UnrecognizedPr opertyException: Unrecognized field "nodes" (Class jp.co.agilegroup.magnolia.rest.dto.Node), not marked as ignorable at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@678e4593; line: 1, column: 1134] (through refer ence chain: jp.co.agilegroup.magnolia.rest.dto.Node["nodes"])

JSON のデータを見ると判るのですが、XMLの場合とは異なり nodes という要素が含まれています。Node.java には nodes というフィールドは定義していないため ( XML format にも書いてなかったので )、マッピングできずに例外が発生しているようです。

そもそも Chapter 5. JSON Support : Jersey 1.18 User Guide をちゃんと読むと判るのですが ( 前回の記事執筆時にはちゃんと読み切れてませんでした。m(_._)m )、JSONConfiguration.FEATURE_POJO_MAPPING を True に設定した場合、5.1. POJO support が有効になります。
しかし、本来行いたかったのは JAXBベースのマッピング ( 5.2. JAXB Based JSON support ) であり、余計なコードを挿入したおかげで別の例外の発生を招いてしまった事になります。

※POJO support の場合でも、Node.java に nodes フィールド ( 及び、setter/getter ) を追加する事で、コードは実行可能になりました。

まとめ

Java で JSON 形式のデータを利用する場合も、Jersey + JAXB を利用する事で Javaオブジェクトへのマッピングが簡単に行えます。

JAXBベースのJSONサポートは非常に強力だと思いますが、Chapter 5. JSON Support : Jersey 1.18 User Guide に以下の記載があるように欠点もあります。

A disadvantage of JAXB based approach could be if you need to work with a very specific JSON format. Then it could be difficult to find a proper way to get such a format produced and consumed. This is a reason why a lot of configuration options are provided, so that you can control how things get serialized out and deserialized back.

非常に特殊な JSON フォーマットの場合、対応できない可能性がある事です。

設定のためのオプションで解決可能な場合が多いでしょうが、どうしても難しい場合 5.3. Low-Level JSON support を利用する必要があるかもしれません。

とはいえ、これはレアケースであり、XML → Java オブジェクトへのマッピングができれば JSON → Javaオブジェクトへのマッピングも jersey-json ライブラリの追加のみで行える場合が殆どではないかと思います。

Java からは JSON は扱いにくいといった話もあるようですが、ここまで三回のコードを見てもそれほど大変という気はしません。

Java を利用している場合でも、JSON 恐るるに足らず。

どんどん使っていきましょう。

次回【第四回】は、POST, PUT, DELETE メソッドの利用についてです。