[Java] Gsonを使ってJSONをパースする

2017.01.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

ウェブ上の外部サイトからデータを取得してデータベースに登録したい場合にどうするかを考えました。JSONのパースするライブラリはいくつか有りますが、今回はGoogle提供のGsonを使用してみたので、調査結果を書いておきます。

GSON

使う場合は下記から取得するかMaven、Gradleなどに追記します。Gradleの場合、compile('com.google.code.gson:gson')でOKです。
GitHub | google / gson

環境

Mac OSX 10.10.5 Yosemite Java 1.8.0_91 Eclipse Mars.2 4.5.2

目的

ローカルに配置した天気情報のJSONを取得してデータベースに登録するため、JSONをパースする。

天気情報を記載したJSON(weather.json)

{
  "title": "東京都千代田区",
  "forecasts": [
    {
      "dateLabel": "今日",
      "telop": "晴れ",
      "date": "2017-01-01",
      "temperature": {
        "min": null,
        "max": {
          "celsius": "6",
          "fahrenheit": "42.8"
        }
      }
    },
    {
      "dateLabel": "明日",
      "telop": "曇時々晴",
      "date": "2017-01-02",
      "temperature": {
        "min": {
          "celsius": "-3",
          "fahrenheit": "26.6"
        },
        "max": {
          "celsius": "9",
          "fahrenheit": "48.2"
        }
      }
    },
    {
      "dateLabel": "明後日",
      "telop": "曇時々雪",
      "date": "2017-01-03",
      "temperature": {
        "min": null,
        "max": null
      }
    }
  ],
  "description": {
    "text": " おおむね晴れるでしょう。"
  }
}

ローカルの任意の場所に配置します。
下記のサイトから取得できる天気情報のJSONを元にしました。
livedoor天気情報

コード

Weatherクラス

package com.external.xxx;

import java.sql.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Weather {

	private Long id;
	private String area;
	private String label;
	private String telop;
	private Integer min_temperature;
	private Float min_fahrenheit;
	private Integer max_temperature;
	private Float max_fahrenheit;
	private String comment;
	private Date yyyymmdd;
	
}

ここに天気情報を格納していきます。
変数は上から順に、地域、今日明日明後日、天候、最低気温、最低華氏、最高気温、最低華氏、コメント、年月日。
Getter/SetterはLombokに任せています。
Date型がjava.sql.Dateなのは後ほど説明します。

パーサー

package com.external.xxx;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

public class JsonGet {
	
	//JSONのパス
	private final String PATH = "/Users/user/Documents/Mac/BLOG/JSON/weather.json";

	/**
	 * JSONを取得してパースしてリストで返す。
	 */
	public List<Weather> execute() {
		String json = local();
		if (json.length()>0) {
			return parser(json);
		}
		return null;
	}

	/**
	 * ローカルのJSONを取得する。
	 * @return
	 */
	public String local() {
		try (Stream<String> stream = Files.lines(Paths.get(PATH));) {

			StringBuilder sb = new StringBuilder();
			stream.forEach(sb::append);			
			return sb.toString();
			
		} catch (IOException e) {
		}
		return null;
	}

	/**
	 * JSONパーサー。
	 */
	public List<Weather> parser(String json) {
		List<Weather> list = new ArrayList<>();
		
		//JsonObject {}
		JsonObject jsonObj = (JsonObject) new Gson().fromJson(json, JsonObject.class);

		//JsonArray []
		JsonArray jsonAry = jsonObj.get("forecasts").getAsJsonArray();
		String ary = jsonAry.get(0).toString();

		JsonObject temperature, obj, jObj1, jObj2;
		Integer min = null, max = null;
		Float minFah = null, maxFah = null;
		String name, telop, area=null, comment=null;

		//値を取得してListに追加
		for (int i = 0; i < jsonAry.size(); i++) {
			obj = jsonAry.get(i).getAsJsonObject();
			//地域
			area = jsonObj.get("title").getAsString();
			//いつ
			name = obj.get("dateLabel").getAsString();
			//天候
			telop = obj.get("telop").getAsString();
			//気温と華氏
			temperature = obj.get("temperature").getAsJsonObject();
			jObj1 = temperature.getAsJsonObject();
			//最低気温
			min = null;
			minFah = null;
			if (!jObj1.get("min").isJsonNull()) { //nullでない場合
				jObj2 = jObj1.get("min").getAsJsonObject();
				min = jObj2.get("celsius").getAsInt();
				minFah = jObj2.get("fahrenheit").getAsFloat();
			}
			//最高気温
			max = null;
			maxFah = null;
			if (!jObj1.get("max").isJsonNull()) { //nullでない場合
				jObj2 = jObj1.get("max").getAsJsonObject();
				max = jObj2.get("celsius").getAsInt();
				maxFah = jObj2.get("fahrenheit").getAsFloat();
			}
			//コメント
			comment = jsonObj.get("description").getAsJsonObject().get("text").getAsString();
			//日付
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			Date date = null;
			try {
				date = sdf.parse((obj.get("date").getAsString()));
			} catch (ParseException e) {
				e.printStackTrace();
			}
			//LISTに追加
			list.add(new Weather(null,area,name,telop,min,minFah,max,maxFah,comment,date));
		}

		return list;
	}

}

メソッドごとの解説は下記の通りです。

execute() - JSONを取得してパースしてリストで返す。
作成したメソッドlocal()とparser()を呼び出してデータベースに登録できる形のリストを作成して返します。

local() - ローカルのJSONを取得する。
パスを入力して取得したらString型で返します。

parser() - JSONパーサー。
まず、JSONを「{ }」ならオブジェクトJsonObject、「[ ]」なら配列JsonArrayとして取得し、その後、ループで値を一つずつ取得していきます。
JSONを取得する際に、配列以外はキーと値の関係となっているのでキーを文字列で指定する必要が有ります。配列の場合は数値です。
この中で最低気温と最高気温の場合のみ値が存在しない場合があるので、ifで条件分岐しています。
isJsonNull()を使用しているのはNullPointerExceptionを回避するためです。

最後にListに追加して返します。

さいごに

JSONの読み方が分かってしまえば大した事では無いので、取得する予定のJSONを整形サービスなどで確認しておくと良いと思います。
個人的にはGoogle Chromeのアプリが便利でした。
Google Chrome ウェブストア | JSON Editor