JSP & Servlet-13日目(WebAPIの利用、JSONパース)

JSP&Servlet

お天気情報を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がもっている画像情報クラス)

01package model;
02 
03import java.io.Serializable;
04 
05public class Image implements Serializable{
06  /*
07  "width": 50,
08  "url": "http://weather.livedoor.com/img/icon/15.gif",
09  "title": "雨",
10  "height": 31
11  */
12  private int width;
13  private String url;
14  private String title;
15  private int height;
16  public Image(){}
17  public int getWidth() {
18    return width;
19  }
20  public void setWidth(int width) {
21    this.width = width;
22  }
23  public String getUrl() {
24    return url;
25  }
26  public void setUrl(String url) {
27    this.url = url;
28  }
29  public String getTitle() {
30    return title;
31  }
32  public void setTitle(String title) {
33    this.title = title;
34  }
35  public int getHeight() {
36    return height;
37  }
38  public void setHeight(int height) {
39    this.height = height;
40  }
41   
42   
43}

●model.Forecast.java(1日分のお天気データクラス)
(Jsonにあるtemperatureは今回不使用)

01package model;
02 
03import java.io.Serializable;
04/*
05 * {
06"dateLabel": "今日",
07"telop": "雨",
08"date": "2017-09-17",
09"temperature": {
10"min": null,
11"max": null
12},
13"image": {
14"width": 50,
15"url": "http://weather.livedoor.com/img/icon/15.gif",
16"title": "雨",
17"height": 31
18}
19}
20 */
21 
22public class Forecast implements Serializable{
23  private String dateLabel;
24  private String telop;
25  private String date;
26  private Image image;
27  public Forecast(){}
28  public String getDateLabel() {
29    return dateLabel;
30  }
31  public void setDateLabel(String dateLabel) {
32    this.dateLabel = dateLabel;
33  }
34  public String getTelop() {
35    return telop;
36  }
37  public void setTelop(String telop) {
38    this.telop = telop;
39  }
40  public String getDate() {
41    return date;
42  }
43  public void setDate(String date) {
44    this.date = date;
45  }
46  public Image getImage() {
47    return image;
48  }
49  public void setImage(Image image) {
50    this.image = image;
51  }
52   
53}

●model.Weather.java(本体となるクラス、3日分の天気などをhas-aで持つ)
(使いたい部分を抽出してクラスのフィールドとする)

01package model;
02 
03import java.io.Serializable;
04/*
05 * "pinpointLocations": [],
06"link": "http://weather.livedoor.com/area/forecast/130010",
07"forecasts": [],
08"location": {},
09"publicTime": "2017-09-17T17:00:00+0900",
10"copyright": {},
11"title": "東京都 東京 の天気",
12"description": {
13"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込みです。",
14"publicTime": "2017-09-17T16:55:00+0900"
15}
16 */
17 
18public class Weather implements Serializable{
19  private String title;
20  private String description;
21  private Forecast[] forecasts;
22  public Weather(){}
23  public String getTitle() {
24    return title;
25  }
26  public void setTitle(String title) {
27    this.title = title;
28  }
29  public String getDescription() {
30    return description;
31  }
32  public void setDescription(String description) {
33    this.description = description;
34  }
35  public Forecast[] getForecasts() {
36    return forecasts;
37  }
38  public void setForecasts(Forecast[] forecasts) {
39    this.forecasts = forecasts;
40  }
41   
42}

controller

1.リクエストを処理するコントローラーを以下のように作成する。
[処理の流れ]
WebAPIにHttpリクエストを投げて、その結果(Json)をInputStreamで取得。
取得したデータをGsonでパースしてWeatherオブジェクトを生成し、リクエストスコープに詰めている。

なお、Jsonに含まれる改行コードの部分をbrタグに変換するメソッドを別メソッドにしている。
(こうすることで改行コード部分をHtml上で改行させることができる)
●controller.Main.java(Servlet)

01package controller;
02 
03import java.io.IOException;
04import java.io.InputStream;
05import java.io.InputStreamReader;
06import java.net.HttpURLConnection;
07import java.net.URL;
08 
09import javax.servlet.RequestDispatcher;
10import javax.servlet.ServletException;
11import javax.servlet.annotation.WebServlet;
12import javax.servlet.http.HttpServlet;
13import javax.servlet.http.HttpServletRequest;
14import javax.servlet.http.HttpServletResponse;
15 
16import com.google.gson.Gson;
17import com.google.gson.JsonArray;
18import com.google.gson.JsonObject;
19import com.google.gson.stream.JsonReader;
20 
21import model.Forecast;
22import model.Image;
23import model.Weather;
24 
25/**
26 * Servlet implementation class Main
27 */
28@WebServlet("/Main")
29public class Main extends HttpServlet {
30  private static final long serialVersionUID = 1L;
31 
32  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
33    //webAPIのurlからURLインスタンスを作成
34    URL url=new URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010");
35    //GETでの通信処理
36    HttpURLConnection con=(HttpURLConnection)url.openConnection();
37    con.setRequestMethod("GET");
38    //InputStreamで結果を取得
39    InputStream is=con.getInputStream();
40    //スプーンからスコップ
41    InputStreamReader isr=new InputStreamReader(is,"UTF-8");
42    //ファイル読み込み時にはBufferedReaderだがここではJsonReaderインスタンスを取得する。
43    JsonReader reader=new JsonReader(isr);
44    //ルートが{}なのでオブジェクトとして取得
45    JsonObject root=new Gson().fromJson(reader,JsonObject.class);
46    //結果として必要となるWeatherインスタンスをnew(フィールドの値はすべてnull)
47    Weather w=new Weather();
48    //Jsonからプロパティがtitleの項目を探してきてそれをStringに変換してwにセットする。
49    w.setTitle(root.get("title").getAsString());
50    //descriptionはオブジェクトを値として持っているのでまずはそれを取得し、その中にあるtextを取得(メソッドチェーン)
51    //結果の文字列に改行文字が含まれているのでそれを<br>という文字列に変換しておく。(メソッドは下部にある)
52    w.setDescription(nl2br(root.get("description").getAsJsonObject().get("text").getAsString()));
53    //forecastsは配列なので配列として取得
54    JsonArray fArray=root.get("forecasts").getAsJsonArray();
55    //Forecast型のインスタンスを格納する配列を準備
56    Forecast[] forecasts=new Forecast[fArray.size()];
57    //配列とforは刺身と醤油の相性。JsonArrayの要素数はsize()で求められる
58    for(int i=0;i<fArray.size();i++) {
59      //配列の要素一つ一つはオブジェクトなのでそれを取得
60      JsonObject fObj=fArray.get(i).getAsJsonObject();
61      //取得した情報をもとにインスタンスを生成したいのでまずはフィールドがnullの状態でnew
62      Forecast f=new Forecast();
63      //JSONから情報を取得し、fにセットしていく
64      f.setTelop(fObj.get("telop").getAsString());
65      f.setDateLabel(fObj.get("dateLabel").getAsString());
66      f.setDate(fObj.get("date").getAsString());
67      //パラメータimageはオブジェクトなのでJsonオブジェクトとして取得
68      JsonObject iObj=fObj.get("image").getAsJsonObject();
69      //その情報をもとにインスタンスを作りたいのでまずはnew
70      Image image=new Image();
71      //情報をもとにimageに詰める
72      image.setHeight(iObj.get("height").getAsInt());
73      image.setTitle(iObj.get("title").getAsString());
74      image.setUrl(iObj.get("url").getAsString());
75      image.setWidth(iObj.get("width").getAsInt());
76      //image要素ができたのでfにセットする
77      f.setImage(image);
78      //fが一つできたので配列にセットする
79      forecasts[i]=f;
80    }
81    //for文が回り終わるとすべてのforecastインスタンスが詰まっているのでおおもとのwにセット
82    w.setForecasts(forecasts);
83    //完成したw(Weater)インスタンスをリクエストスコープに詰める
84    request.setAttribute("weather", w);
85    //フォワード処理
86    RequestDispatcher rd=request.getRequestDispatcher("/WEB-INF/view/main.jsp");
87    rd.forward(request,response);
88 
89  }
90 
91  //文字列に含まれる改行コードを<br>タグに置き換えるメソッド
92    public static String nl2br(String str) {
93        if (str == null || str.equals("")) {
94          return "";
95        }
96        str = str.replace("\n", "<br>");
97        return str;
98      }
99}

view

1.modelとcontrollerの連携により欲しいデータが作れたので後はviewで表示するだけだ。
いつものように/WEB-INF/の中にviewフォルダを作ってmain.jspを配置しよう。
●/WEB-INF/view/main.jsp

01<%@ page language="java" contentType="text/html; charset=UTF-8"
02    pageEncoding="UTF-8" import="model.*"%>
03<%
04Weather w=(Weather)request.getAttribute("weather");
05 
06%>
07<!DOCTYPE html>
08<html>
09<head>
10<meta charset="UTF-8"/>
11<title><%=w.getTitle() %></title>
12</head>
13<body>
14<h1><%=w.getTitle() %></h1>
15<p><%=w.getDescription() %></p>
16<table border="1">
17<%for(Forecast f:w.getForecasts()) {%>
18<tr>
19<td><%=f.getDateLabel() %></td>
20<td><%=f.getTelop() %></td>
21<td><%=f.getDate() %></td>
22<%Image image=f.getImage(); %>
23<td><img src="<%=image.getUrl()%>" width="<%=image.getWidth() %>" height="<%=image.getHeight() %>" alt="<%=image.getTitle()%>"></td>
24</tr>
25<%} %>
26</table>
27</body>
28</html>

完成品

1.以下のように東京の3日分(時間帯によっては2日分)のお天気情報が表示されれば成功だ。

コメント

タイトルとURLをコピーしました