お天気情報をJSONでくれるWebAPIがあるので、それを利用してお天気アプリを作成しよう。
WebAPI確認
1.まずはWebAPIから吐き出されるJSONを確認しよう。
今回はlivedoorから提供されているWeather Hacks
のAPIを使用する。まずは東京のお天気情報のリクエストURLを叩いてレスポンスを確認しよう。
http://weather.livedoor.com/forecast/webservice/json/v1?city=130010
まるっとコピーしてJSON整形サイトで確認してもよいが、吐き出す内容が多いのでChromeにプラグインとしてインストールしたJSON formatterで確認してみる。
要素を折りたたむことができるので全体像をつかむのに便利だ。
2.アプリの仕様を決める。
すべての要素を盛り込んでアプリを作ってもいいが、今回は送信されるデータのうち、title,description,forecastsを利用することとする。
アプリ作成
下準備
1.エクリプス、新規動的Webプロジェクトから[joytas13]アプリを作成する。
2.GsonでJsonパースを行いたいので以下のファイルをWEB-INF/libの中に貼り付ける。
下からダウンロード
model
1.Jsonデータとにらめっこしながらmodelを作成する。今回は以下のような3つのクラスを作成した。
●model.Image.java(forecastがもっている画像情報クラス)
package model; import java.io.Serializable; public class Image implements Serializable{ /* "width": 50, "url": "http://weather.livedoor.com/img/icon/15.gif", "title": "雨", "height": 31 */ private int width; private String url; private String title; private int height; public Image(){} public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } }
●model.Forecast.java(1日分のお天気データクラス)
(Jsonにあるtemperatureは今回不使用)
package model; import java.io.Serializable; /* * { "dateLabel": "今日", "telop": "雨", "date": "2017-09-17", "temperature": { "min": null, "max": null }, "image": { "width": 50, "url": "http://weather.livedoor.com/img/icon/15.gif", "title": "雨", "height": 31 } } */ public class Forecast implements Serializable{ private String dateLabel; private String telop; private String date; private Image image; public Forecast(){} public String getDateLabel() { return dateLabel; } public void setDateLabel(String dateLabel) { this.dateLabel = dateLabel; } public String getTelop() { return telop; } public void setTelop(String telop) { this.telop = telop; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } }
●model.Weather.java(本体となるクラス、3日分の天気などをhas-aで持つ)
(使いたい部分を抽出してクラスのフィールドとする)
package model; import java.io.Serializable; /* * "pinpointLocations": [], "link": "http://weather.livedoor.com/area/forecast/130010", "forecasts": [], "location": {}, "publicTime": "2017-09-17T17:00:00+0900", "copyright": {}, "title": "東京都 東京 の天気", "description": { "text": " 前線が日本の南に停滞しています。また、大型の台風第18号が四国の南\n西海上にあって、北東へ進んでいます。\n\n【関東甲信地方】\n 関東甲信地方は、おおむね雨となっています。\n\n 17日は、前線や台風の影響によりおおむね雨となり、雷を伴い非常に激\nしく降る所がある見込みです。\n\n 18日は、前線や台風の影響により、はじめは雨で雷を伴い非常に激しく\n降る所がありますが、次第に晴れるでしょう。\n\n 関東近海では、18日にかけて、うねりを伴い大しけとなる見込みです。\n船舶は高波に警戒してください。\n\n【東京地方】\n 17日は、雨で夜は雷を伴い激しく降る所があるでしょう。\n 18日は、曇り後晴れで、明け方まで雨で雷を伴い激しく降る所がある見\n込みです。", "publicTime": "2017-09-17T16:55:00+0900" } */ public class Weather implements Serializable{ private String title; private String description; private Forecast[] forecasts; public Weather(){} public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Forecast[] getForecasts() { return forecasts; } public void setForecasts(Forecast[] forecasts) { this.forecasts = forecasts; } }
controller
1.リクエストを処理するコントローラーを以下のように作成する。
[処理の流れ]
WebAPIにHttpリクエストを投げて、その結果(Json)をInputStreamで取得。
取得したデータをGsonでパースしてWeatherオブジェクトを生成し、リクエストスコープに詰めている。
なお、Jsonに含まれる改行コードの部分をbrタグに変換するメソッドを別メソッドにしている。
(こうすることで改行コード部分をHtml上で改行させることができる)
●controller.Main.java(Servlet)
package controller; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.stream.JsonReader; import model.Forecast; import model.Image; import model.Weather; /** * Servlet implementation class Main */ @WebServlet("/Main") public class Main extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //webAPIのurlからURLインスタンスを作成 URL url=new URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010"); //GETでの通信処理 HttpURLConnection con=(HttpURLConnection)url.openConnection(); con.setRequestMethod("GET"); //InputStreamで結果を取得 InputStream is=con.getInputStream(); //スプーンからスコップ InputStreamReader isr=new InputStreamReader(is,"UTF-8"); //ファイル読み込み時にはBufferedReaderだがここではJsonReaderインスタンスを取得する。 JsonReader reader=new JsonReader(isr); //ルートが{}なのでオブジェクトとして取得 JsonObject root=new Gson().fromJson(reader,JsonObject.class); //結果として必要となるWeatherインスタンスをnew(フィールドの値はすべてnull) Weather w=new Weather(); //Jsonからプロパティがtitleの項目を探してきてそれをStringに変換してwにセットする。 w.setTitle(root.get("title").getAsString()); //descriptionはオブジェクトを値として持っているのでまずはそれを取得し、その中にあるtextを取得(メソッドチェーン) //結果の文字列に改行文字が含まれているのでそれを<br>という文字列に変換しておく。(メソッドは下部にある) w.setDescription(nl2br(root.get("description").getAsJsonObject().get("text").getAsString())); //forecastsは配列なので配列として取得 JsonArray fArray=root.get("forecasts").getAsJsonArray(); //Forecast型のインスタンスを格納する配列を準備 Forecast[] forecasts=new Forecast[fArray.size()]; //配列とforは刺身と醤油の相性。JsonArrayの要素数はsize()で求められる for(int i=0;i<fArray.size();i++) { //配列の要素一つ一つはオブジェクトなのでそれを取得 JsonObject fObj=fArray.get(i).getAsJsonObject(); //取得した情報をもとにインスタンスを生成したいのでまずはフィールドがnullの状態でnew Forecast f=new Forecast(); //JSONから情報を取得し、fにセットしていく f.setTelop(fObj.get("telop").getAsString()); f.setDateLabel(fObj.get("dateLabel").getAsString()); f.setDate(fObj.get("date").getAsString()); //パラメータimageはオブジェクトなのでJsonオブジェクトとして取得 JsonObject iObj=fObj.get("image").getAsJsonObject(); //その情報をもとにインスタンスを作りたいのでまずはnew Image image=new Image(); //情報をもとにimageに詰める image.setHeight(iObj.get("height").getAsInt()); image.setTitle(iObj.get("title").getAsString()); image.setUrl(iObj.get("url").getAsString()); image.setWidth(iObj.get("width").getAsInt()); //image要素ができたのでfにセットする f.setImage(image); //fが一つできたので配列にセットする forecasts[i]=f; } //for文が回り終わるとすべてのforecastインスタンスが詰まっているのでおおもとのwにセット w.setForecasts(forecasts); //完成したw(Weater)インスタンスをリクエストスコープに詰める request.setAttribute("weather", w); //フォワード処理 RequestDispatcher rd=request.getRequestDispatcher("/WEB-INF/view/main.jsp"); rd.forward(request,response); } //文字列に含まれる改行コードを<br>タグに置き換えるメソッド public static String nl2br(String str) { if (str == null || str.equals("")) { return ""; } str = str.replace("\n", "<br>"); return str; } }
view
1.modelとcontrollerの連携により欲しいデータが作れたので後はviewで表示するだけだ。
いつものように/WEB-INF/の中にviewフォルダを作ってmain.jspを配置しよう。
●/WEB-INF/view/main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="model.*"%> <% Weather w=(Weather)request.getAttribute("weather"); %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title><%=w.getTitle() %></title> </head> <body> <h1><%=w.getTitle() %></h1> <p><%=w.getDescription() %></p> <table border="1"> <%for(Forecast f:w.getForecasts()) {%> <tr> <td><%=f.getDateLabel() %></td> <td><%=f.getTelop() %></td> <td><%=f.getDate() %></td> <%Image image=f.getImage(); %> <td><img src="<%=image.getUrl()%>" width="<%=image.getWidth() %>" height="<%=image.getHeight() %>" alt="<%=image.getTitle()%>"></td> </tr> <%} %> </table> </body> </html>
完成品
1.以下のように東京の3日分(時間帯によっては2日分)のお天気情報が表示されれば成功だ。
コメント