「プロになるJava」の補足的記事。
JSONはデータ交換で広く使われる形式で、機械にも人間にも読み書きしやすいということでよく使われています。 ということでJSONを扱うライブラリを使ってみましょう。
jp-postal-code-api
今回、扱うJSONとしては郵便番号から住所を得るWeb APIであるjp-postal-code-apiのデータを使います。
https://github.com/ttskch/jp-postal-code-api
Web APIはHTTPを介して呼び出すAPIで、JSONでやりとりすることが多いです。
ここでの住所データの形式はこのようになっています。
{ "postalCode": "1000014", "addresses": [ { "prefectureCode": "13", "ja": { "prefecture": "東京都", "address1": "千代田区", "address2": "永田町", "address3": "", "address4": "" }, "kana": { "prefecture": "トウキョウト", "address1": "チヨダク", "address2": "ナガタチョウ", "address3": "", "address4": "" }, "en": { "prefecture": "Tokyo", "address1": "Chiyoda-ku", "address2": "Nagatacho ", "address3": "", "address4": "" } } ] }
Jackson
JSONを扱うときによく使われるライブラリがJacksonです。
https://github.com/FasterXML/jackson
説明を見るとDatabindというのがデータの結び付けができそう(bindは結び付けという意味です)。
dependencyの追加
ということでJackson Databindを見てみます。
https://github.com/FasterXML/jackson-databind
次のようにdependencyを書けばいいということが書いてあります。
今回はバージョンを直接かいて、次のようなタグをpom.xmlに追加します。
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.17.1</version> </dependency> </dependencies>
Maven Dependenciesを見ると、jackson-databindだけではなくjackson-coreやjackson-annotasionsという、jackson-databindが依存したライブラリも読み込まれています。
テストデータの埋め込み
PostalCodeJSONSample
というクラスを作って、まずはJSONを文字列でもっておきます。
public class PostalCodeJSONSample { static String jsonFile = """ { "postalCode": "1000014", "addresses": [ { "prefectureCode": "13", "ja": { "prefecture": "東京都", "address1": "千代田区", "address2": "永田町", "address3": "", "address4": "" }, "kana": { "prefecture": "トウキョウト", "address1": "チヨダク", "address2": "ナガタチョウ", "address3": "", "address4": "" }, "en": { "prefecture": "Tokyo", "address1": "Chiyoda-ku", "address2": "Nagatacho ", "address3": "", "address4": "" } } ] } """; }
JSONに結びつけるrecordの作成
次のようなrecordでJSONに対応させます。
record Area(String prefecture, String address1, String address2, String address3, String address4) {} record Address(String prefectureCode, Area ja, Area kana, Area en) {} record PostalData(String postalCode, List<Address> addresses) {}
Listを使うので次のようなimportを追加
import java.util.List;
JacksonでのJSON読み込み
JacksonでJSONをJavaクラスに対応させるには、まずObjectMapper
のオブジェクトを作って、readValue
メソッドにJSONデータとJavaクラスを渡して呼び出します。Javaクラスを渡すときはクラス名に.class
をつけてクラスのオブジェクトを渡す必要があります。
public static void main(String[] args) throws JsonProcessingException { var mapper = new ObjectMapper(); PostalData data = mapper.readValue(jsonFile, PostalData.class); System.out.println(data); }
JsonProcessingException
をthrowsにつけるので、importには次のような2つが必要です。
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper;
そうすると、次のようにJSONファイルをPostalData
のオブジェクトとして扱えていることがわかります。
PostalData[postalCode=1000014, addresses=[Address[prefectureCode=13, ja=Area[prefecture=東京都, address1=千代田区, address2=永田町, address3=, address4=], kana=Area[prefecture=トウキョウト, address1=チヨダク, address2=ナガタチョウ, address3=, address4=], en=Area[prefecture=Tokyo, address1=Chiyoda-ku, address2=Nagatacho , address3=, address4=]]]]
コード全体は次のようになります。
package projava; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; public class PostalCodeJSONSample { record Area(String prefecture, String address1, String address2, String address3, String address4) {} record Address(String prefectureCode, Area ja, Area kana, Area en) {} record PostalData(String postalCode, List<Address> addresses) {} public static void main(String[] args) throws JsonProcessingException { var mapper = new ObjectMapper(); PostalData data = mapper.readValue(jsonFile, PostalData.class); System.out.println(data); } static String jsonFile = """ { "postalCode": "1000014", "addresses": [ { "prefectureCode": "13", "ja": { "prefecture": "東京都", "address1": "千代田区", "address2": "永田町", "address3": "", "address4": "" }, "kana": { "prefecture": "トウキョウト", "address1": "チヨダク", "address2": "ナガタチョウ", "address3": "", "address4": "" }, "en": { "prefecture": "Tokyo", "address1": "Chiyoda-ku", "address2": "Nagatacho ", "address3": "", "address4": "" } } ] } """; }
Retrofit
次に、HTTPを介してWeb APIを呼び出してみましょう。HttpClientを使ってHTTPアクセスを行ってもいいのですが、ここではWeb APIをJavaメソッドとして呼び出せるライブラリのRetrofit2を使います。
https://square.github.io/retrofit/
dependencyの追加
Downloadのところを見ると、Mavenのdependensyの書き方があるので、これを使います。
また、Jacksonを使ったコンバータを使うときにはconverter-jackson
を使うようなことが書いてありますね。
最新のバージョンはgithubを見にいくと2.11.0のようです。
ということでdependencyとしては次の2つを追加。
<dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>retrofit</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>converter-jackson</artifactId> <version>2.11.0</version> </dependency>
APIに結びつけるinterfaceの作成
今回は次のようなAPIを呼び出します。
URLの一部がパラメータによって変わりますね。
次のようなJavaメソッドとして呼び出したいとしますね。
PostalData getAddress(String code)
そうすると、次のようなinterfaceを定義することになります。
interface PostalAPI { @GET("api/v1/{postalCode}.json") Call<PostalData> getAddress(@Path("postalCode") String code); }
ここで、メソッドの戻り値はCall
にして、ジェネリクスとして型を指定します。このとき、okhttp3にもCall
があるので、retrofit2のほうをimportするよう気を付けてください。
また、呼び出したいURLからドメイン名よりあとの部分を@GET
アノテーションに指定します。パラメータの部分は中カッコで囲って適当な名前をつけておきます。ここではpostalCode
とします。
@GET("api/v1/{postalCode}.json")
引数に先ほどの名前を@Path
アノテーションで指定します。
@Path("postalCode") String code
今回はPostalCodeAPISampleというクラスにすべてを書くので、コードは次のようになります。
package projava; import projava.PostalCodeJSONSample.PostalData; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Path; public class PostalCodeAPISample { interface PostalAPI { @GET("api/v1/{postalCode}.json") Call<PostalData> getAddress(@Path("postalCode") String code); } }
呼び出し
まずRetrofitのオブジェクトを作成します。このときBuilderを使って構築が必要で、次のようなコードになります。
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://jp-postal-code-api.ttskch.com") .addConverterFactory(JacksonConverterFactory.create()) .build();
Javaでは名前付き引数がないので、省略可能なたくさんの引数が必要になるときには、代わりにこのようなBuilderが使われることがあります。
Retrofitオブジェクトのcreateメソッドにインタフェースを渡すと、Web APIを呼び出せるオブジェクトが返ってきます。
PostalAPI api = retrofit.create(PostalAPI.class);
メソッドを呼び出すと、実行に必要なCallオブジェクトが返ってきて、execute()
をすると実行されてbody()
メソッドで結果が得れます。
PostalData data = api.getAddress("1600023").execute().body(); System.out.println(data);
次のように、Web APIの呼び出しが行えました。
PostalData[postalCode=1600023, addresses=[Address[prefectureCode=13, ja=Area[prefecture=東京都, address1=新宿区, address2=西新宿, address3=, address4=], kana=Area[prefecture=トウキョウト, address1=シンジュクク, address2=ニシシンジュク, address3=, address4=], en=Area[prefecture=Tokyo, address1=Shinjuku-ku, address2=Nishishinjuku , address3=, address4=]]]]
ただ、Retrofitは呼び出すとプロセスがしばらく終わらないので、気になる場合は終了させてください。
コード全体は次のようになります。
package projava; import java.io.IOException; import projava.PostalCodeJSONSample.PostalData; import retrofit2.Call; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; import retrofit2.http.GET; import retrofit2.http.Path; public class PostalCodeAPISample { interface PostalAPI { @GET("api/v1/{postalCode}.json") Call<PostalData> getAddress(@Path("postalCode") String code); } public static void main(String[] args) throws IOException { var retrofit = new Retrofit.Builder() .baseUrl("https://jp-postal-code-api.ttskch.com") .addConverterFactory(JacksonConverterFactory.create()) .build(); var api = retrofit.create(PostalAPI.class); var data2 = api.getAddress("1600023").execute().body(); System.out.println(data2); } }