JSONをVolleyで解析表示する | JSON Volley Google Maps Geocoding API | Android

スポンサーリンク

JSONとは

地図の住所情報や、天気の情報をインターネットから取得したい場合はJSONデータを扱います。

例えば、東京都庁についてのGoogle Maps Geocoding API 情報を取得したければ

https://maps.googleapis.com/maps/api/geocode/json?address=%E6%9D%B1%E4%BA%AC%E9%83%BD%E5%BA%81&language=JP

を表示します。結果として、

{
   "results" : [
      {
         "address_components" : [
            {
               "long_name" : "1",
               "short_name" : "1",
               "types" : [ "premise" ]
            },
            {
               "long_name" : "8",
               "short_name" : "8",
               "types" : [ "political", "sublocality", "sublocality_level_4" ]
            },
     -------略------------
            {
               "long_name" : "日本",
               "short_name" : "JP",
               "types" : [ "country", "political" ]
            },
            {
               "long_name" : "163-8001",
               "short_name" : "163-8001",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "日本、〒163-8001 東京都新宿区西新宿2丁目8−1",
         "geometry" : {
            "location" : {
               "lat" : 35.6896342,

  --------------略-------------
            "local_government_office",
            "point_of_interest"
         ]
      },
      {
         "address_comp
   ----------- 略 -----------
   "status" : "OK"
}

となります。

この帰ってきたデータの形式がJSONであり、このデータをVolleyを使って解析し、Androidアプリで活用します。

AndroidでVolleyの実装

実装は簡単です。gradleに以下を追加するだけです。

implementation 'com.android.volley:volley:1.1.0'

古いgradleでは

compile 'com.android.volley:volley:1.1.0'

書き加えて、gradleを更新します。

ActivityのJavaで

JsonObjectRequest adReq

と試しに記述をして、問題なければ成功です。

また、インターネットにアクセスして情報を取得するアプリとなりますから

Manifestに以下を追記します。

<uses-permission android:name="android.permission.INTERNET" />

JSON解析を行うために次のクラスを追加します。

public class AppController extends Application {

    public static final String TAG = AppController.class.getSimpleName();

    private RequestQueue mRequestQueue;

    private static AppController mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized AppController getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }
    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
}

さらにこのAppControllerについて、Manifestに登録します。

<application
    android:name="***.***.jsonactivity.AppController"
    android:allowBackup="true"
    ---略---  >
</application>

特に理屈を考えず、AppControllerとManifestの登録が必要だ、と覚えてしまって問題ありません。

準備のまとめ

gradleにVolleyを追加する

ManifestにINTERNETを追加

AppControllerクラスを追加

ManifestにAppControllerを設定

どれか一つ、意外と忘れてしまいたちなので忘れずに設定してください。

住所を取得する方法(formatted_address)

住所を取得する際、アドレスに日本語文字は使えませんので、URL用に文字をエンコードす必要があります。

東京都庁  →  %E6%9D%B1%E4%BA%AC%E9%83%BD%E5%BA%81

といった具合の変換です。

変換方法は、

String placeString = "東京都庁";
String encodedResult;
try{
    encodedResult= URLEncoder.encode(placeString, "UTF-8");
}catch (Exception e){
    encodedResult ="";
}

となります。 try-catchが必要です。

続けてJSONの結果の言語を指定します。

Locale locale = Locale.getDefault();
String language = locale.getLanguage();

として、端末で使用している言語を取得します。

その上で取得用アドレスに言語を設定します。

String url = "https://maps.googleapis.com/maps/api/geocode/json?address="
        +encodedResult+"&language="+language+"&key="+"YOUR_KEY";

Android、この地図JSONも共にgoogleですから、Localeで取得した言語とアドレスで指定する言語は同じで、このように設定することができます。

では実際に「formatted_address」を取得してみます。

{
   "results" : [
      {
         "address_components" : [
            {
               "long_name" : "1",
       --------------略-----------------
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "日本、〒163-8001 東京都新宿区西新宿2丁目8−1",
[]は配列、{はオブジェクト となります。

results配列の0個目、のformatted_addressを取得 するということになります。

String url = "https://maps.googleapis.com/maps/api/geocode/json?address="
        +encodedResult+"&language="+language;
JsonObjectRequest adReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener <JSONObject>() {
    @Override
    public void onResponse(JSONObject response) {
        try {
            addressString = response.getJSONArray("results").getJSONObject(0).getString("formatted_address");
            Log.d("Log0",addressString);
        } catch (JSONException e) {
            Log.d("Log0", "" + e);
        }
    }
},
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                VolleyLog.d("Log0", "Error: " + error.getMessage());
            }
        }
);
AppController.getInstance().addToRequestQueue(adReq);

とします。 addressStringは Activity冒頭で定義しておきます。

郵便番号を取得する方法(postal_code)

見てみると返されたJSONのpostal_codeは address_components の中にあります。

{
   "results" : [
      {
         "address_components" : [
            {
               "long_name" : "1",
               "short_name" : "1",
               "types" : [ "premise" ]
            },
 --------------略----------------
            {
               "long_name" : "163-8001",
               "short_name" : "163-8001",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "日本、〒163-8001 東京都新宿区西新宿2丁目8−1",

階層を追ってみてみると

results配列の0個目のaddress_components配列のtypesがpostal_codeのlong_name、となります。

どう書くのかというと、

for (int i = 0; i < response.getJSONArray("results").getJSONObject(0)
                   .getJSONArray("address_components").length(); i++) {
    typesString = response.getJSONArray("results").getJSONObject(0).
           getJSONArray("address_components").getJSONObject(i).getString("types");
    longNameString = response.getJSONArray("results").getJSONObject(0).
           getJSONArray("address_components").getJSONObject(i).getString("long_name"); 
    if (typesString.contains("postal_code")) {
           Log.d("Log0",longNameString);
   }
}

少し面倒ですが、階層をわかりやすく。

address_componentsの中身を順にみて行って、typeがpostal_codeであるときの、long_nameを取得しています。

結果として、郵便番号が表示されます。

formatted_addressから郵便番号や国名を削除したい場合はこのpostal_codeya

countryを取得してreplaceして削除すれば良い。ということいなります。

addressString = addressString.replace(longNameString, "");

といった具合です。

実際はMAPを使うとよいので、いずれそのコードも。今回は使っておりません。

ソースコード

public class MainActivity extends AppCompatActivity {
    String addressString;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Locale locale = Locale.getDefault();
        String language = locale.getLanguage();
        String placeString = "東京都庁";
        String encodedResult;
        try{
            encodedResult= URLEncoder.encode(placeString, "UTF-8");
        }catch (Exception e){
            encodedResult ="";
        }
        String url = "https://maps.googleapis.com/maps/api/geocode/json?address="
                +encodedResult+"&language="+language;
        JsonObjectRequest adReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    addressString = response.getJSONArray("results").getJSONObject(0).getString("formatted_address");
                    Log.d("Log0",addressString);
                    String longNameString;
                    String typesString;

                    for (int i = 0; i < response.getJSONArray("results").getJSONObject(0).getJSONArray("address_components").length(); i++) {
                        typesString = response.getJSONArray("results").getJSONObject(0).
                                getJSONArray("address_components").getJSONObject(i).getString("types");
                        longNameString = response.getJSONArray("results").getJSONObject(0).
                                getJSONArray("address_components").getJSONObject(i).getString("long_name"); //typesに対応したlong_name
                        if (typesString.contains("postal_code")) {
                              addressString = addressString.replace(longNameString, ""); //以下はアドレスに含まれる余分な情報を削除する
                        }
                        if (typesString.contains("country")) {
                            addressString = addressString.replace(longNameString, "");
                        }
                    addressString = addressString.replace("〒 ", "");//郵便番号記号の削除
                    addressString = addressString.replace("、", "");
                    Log.d("Log0",addressString);
                } catch (JSONException e) {
                    Log.d("Log0", "" + e);
                }
            }
        },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        VolleyLog.d("Log0", "Error: " + error.getMessage());
                    }
                }
        );
        AppController.getInstance().addToRequestQueue(adReq);
}

結果は

D/Log0: 日本、〒163-8001 東京都新宿区西新宿2丁目8−1
D/Log0: 東京都新宿区西新宿2丁目8−1

となるはずです。

googleMapと組み合わせて住所を取得する方法は次の機会に!