1. (What) JSONとは何か
2. (Why) なぜJSONを使うのか
- 2-1. データベース的な視点から
- 2-2. Web的な視点から
- 2-3. モバイルアプリでなぜJSONを使うのか
1. JSONとは何か
JSON(ジェイソン、JavaScript Object Notation)は、JavaScriptにおけるオブジェクトの表記法をベースとした軽量なデータ記述言語である。JavaScript Object Notation - Wikipedia
とりあえずは「JSONオブジェクト」というのを知っておけばOK。JSONオブジェクトは一般的に連想配列と言われる類のもので、以下のように記述する。
{"name": "John Smith", "age": 33}
つまり {"キー":"値"}の形になっている。(ダブルクオーテーションが付いていることに注意。つまりキーは必ず"文字列")
こういう形式は他の言語ではディクショナリ(Python)とかハッシュ(Ruby, Perl)などと呼称されている。ちなみにJavaでは連想配列を扱うクラスとしてHashMapがある。HashMapクラス - コレクション(HashMap) - Java入門
平たく言えば、「いろんな言語で統一的に扱えるデータ型があると便利ですよ、そこでJSONを使いましょう」ということ。(JSONはJavaScript以外の言語でも使える)
統一的に扱えるならXMLとか使ってもいいんだけど、JSONがよく使われている印象があります。(詳しい人教えてください)
2. なぜJSONを使うのか
上で述べたように、いろんな言語間でデータを取り回すために使います。
2-1. データベース的な視点から
上で述べたようにJSONオブジェクトは辞書形式です。これはデータベースと親和性が高いです。
といっても意味がわからないと思うので解説します。
JSONオブジェクトは以下のように書きます。
{"name": "John Smith", "age": 33}
これをデータベース的な視点から考えてみましょう。
まず、データベースとは何でしょうか。といってもそのままですが、データを格納する存在のことです。
実際に使用する際にはデータベースソフトウェアと呼ばれるものを利用します。
(代表的なデータベースソフトウェアにはMySQL、SQLite、PostgreSQL、MongoDBなどがあります。UbuntuでMySQLを使って遊んでみる方法をここで解説しています。UbuntuでMySQLを使う - how to code something)
まずデータベースっていうのは複数のテーブルから構成されます。
そして、そのテーブルは複数のカラムから構成されます。
そして個々のカラムは値を持っています。
図にすると下のような感じです。
例として、ユーザーの名前と年齢などを管理するデータベースを設計してみましょう。
データベース名は、myFirstDatabaseとします。
テーブルですが、ユーザーを管理するテーブルを作りたいので、Userテーブルと名づけます。
カラムですが、名前と年齢なのでそれぞれnameカラムとageカラムとします。
それぞれのカラムに入れる値はJohnと22とします。つまり下のような感じです。
データベース | テーブル | カラム | 値 |
---|---|---|---|
myFirstDatabase | User | name | John |
age | 22 |
何のことはない、つまりデータベースって「表」のことなんですね。
(余談ですが、データベース=表ならExcel使えばいいじゃんって思ってました。データベースソフトウェアの一つの意義は「高速に」データを扱えることです。Excelを開いて値を取り出して、っていうプログラムも書けなくは無いですが、1秒間に数十回もアクセスがあるWebサイトにおいて、データを取り出したり書き込んだりという操作を行うとすると、現実的では無いでしょう)
気づいたかもしれませんが、JSONオブジェクトっていうのは上の図における「カラム - 値の関係」を表現したものになっています。
{"name": "John Smith", "age": 33}
は、
{"カラム1": "値1", "カラム2": "値2"}
ということです。
連想配列の形式をとることで、データベースとの親和性が高くなります。
そもそもデータベースのありがたみがわからないと実感がわかないかもしれませんが、
Ruby on RailsなどのWebフレームワークを使ってデータベースアプリケーションを作ってみると、より意義が納得できるでしょう。
2-2. Web的な視点から
まず、APIという言葉について解説します。
APIとはソフトウェアコンポーネントが互いにやりとりするのに使用するインタフェースの仕様である。アプリケーションプログラミングインタフェース - Wikipedia
例えば、Twitterでツイートを取得して表示するTwitterクライアントアプリを作ることを考えてみましょう。
これにはまず、タイムラインからツイートをとってこないといけません。
例えばオバマ大統領のツイートはここにアクセスすれば取ってこれます。
https://twitter.com/BarackObama
ここからオバマ大統領のツイート(赤枠で囲った部分)を抜き出すには、HTMLファイルをダウンロードしてパース(解析)する必要があります。
これは自分で実装できなくもないのですが、めんどくさいですよね。みんな同じ事をやるのは車輪の再生産再発明なので、これをライブラリとして会社側(Twitter)が提供するのがAPI、ということです。
(筆者は1年前にとあるサイトの全データを1週間かけてダウンロードしてデータベースに突っ込み、自前API的なものを作ったことがあります。怒られそうなのでどこかは書きませんが(穏便にサーバにアクセスしました。念のため)。ちなみにそのサイトは現在はAPIを公開しています。)
AppleとかFacebookとかもAPIを公開しています。APIを公開すると、自分の持っているデータを利用してアプリを作ってもらうことが容易になります。
話が長くなりましたが、このようなAPIはJSONで提供されることが多いです。
オバマ大統領のツイートを表示するTwitterのAPIにアクセスしてみましょう。
https://api.twitter.com/1/statuses/user_timeline.json?screen_name=BarackObama
ずらっと並んでいるので圧倒されますが、これはJSON形式です。
2-3. なぜモバイルアプリでJSONを使うのか
モバイルアプリではAPIを提供するサーバーに問い合わせてデータを取得することが多いです。
でもなぜネットワークに接続しないといけないのでしょうか?
いま、モバイルアプリを便宜的に以下の3通りに分けてみます。
- ネットワークに接続しないといけないアプリ
- ネットワークに接続しなくてもいいけど、接続するアプリ
- 何か理由があってネットワークに接続しない or 単純にめんどくさくてネットワークに接続しないアプリ
1の例としては先に挙げたTwitterアプリが挙げられます。(ネットワークに接続しないとツイートが取得できない)
3についてもいいでしょう。何か理由があってネットワークに接続したくないなら止めません。
問題は2です。結論から言うと、「データベースを動的に変更したいとき、ネットワークに問い合わせてデータを取得する形式は効果的」です。
例えばあるテニスサークルの名簿(名前と学年)を管理するアプリを考えましょう。
上でやったのと同じ容量でデータベースを設計します。
サークルには毎年新しい人が入ってくるので追加しないと行けません。また卒業生は削除するべきでしょう。学年も毎年更新されますね。(※留年しない限りは)
このようなとき、データベースがモバイル端末(iPhoneとかAndroid携帯)の「内部」(=ローカル)にあると、開発者側がデータベースに変更を加えることは難しくなります。(できなくはありませんが、すべての端末で同じデータが共有(=同期)されなくなります)
つまりデータをJSON形式で提供するデータベースアプリケーションをサーバーサイドで作っておけば、モバイルアプリにおいて最新のデータを保つことが容易になります。
3. JSONの使い方
さて、本題の実装に入ります。
JavaでJSONを扱う方法について解説しますが、実装は以下の3段階のプロセスに分けることができます。
デフォルトでもHTTPリクエストを送るクラスは入っているようですが、Androidのチュートリアルでみんながよく使っているHttpGetを使って実装してみます。
ApacheのサイトからHttpClientとHttpCoreのライブラリを落としてきて適当なフォルダに置いておいてください。
下の2つをクリックすればダウンロードできます。
HttpClient 4.3-beta1
HttpCore 4.3-beta1
eclipseでプロジェクトを新規作成し、ビルドパス>ライブラリ>外部JARの追加、として、上の2つを解凍してできたJarファイルを追加しておきます。(これでライブラリをimportできます)
では、今回はホリエモンのツイートを取得して表示するプログラムを書いてみましょう。
ホリエモンのタイムラインには以下からアクセスできます。ホリエモンのscreen nameはtakapon_jpです。
ホリエモンのタイムライン
JSONの構造を見てみます。
膨大なJSONの構造を解読するコツは、一つのキー(この場合は"text")を選び、検索をかけることです。
textというキーが周期的に現れるので、JSONのひとかたまりが把握できるはずです。
(下の図における赤枠で囲った部分)
それではTwitterのAPIにはどんなキーがあるか見てみましょう。
JSONからひとかたまりを取り出してきます。
{"created_at":"Fri Apr 26 03:49:39 +0000 2013","id":327630529061085184,"id_str":"327630529061085184","text":"\u305d\u3053\u304b\uff01 RT @Hiroki_Komazaki: \u5800\u6c5f\u8cb4\u6587\u3055\u3093\u3068\u30ec\u30fc\u30b7\u30c3\u30af\u7e4b\u304c\u308a\u3060\u3063\u305f\u3068\u306f\uff01\u4f55\u304b\u5b09\u3057\u3044w RT @kobe_kanagawa @takapon_jp \u2605\u5800\u6c5f\u69d8\u3068\u99d2\u5d0e\u69d8\u306f\u3001\u30ec\u30fc\u30b7\u30c3\u30af\u7e4b\u304c\u308a\u3067\u3059\u306d\u266a\u4eca\u591c\u306e\u671d\u751f\u300c\u5404\u754c\u3067\u6d3b\u8e8d\u3059\u308b\u7686\u69d8\u306e\u71b1\u3044\u8a0e\u8ad6\u300d\u3092\u697d\u3057\u307f\u306b\u3057\u3066\u3044\u307e\u3059\u2606","source":"\u003ca href=\"http:\/\/www.echofon.com\/\" rel=\"nofollow\"\u003eEchofon\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":44791521,"id_str":"44791521","name":"\u5800\u6c5f\u8cb4\u6587(Takafumi Horie)","screen_name":"takapon_jp","location":"Tokyo","url":"http:\/\/ameblo.jp\/takapon-jp\/","description":"\u516d\u672c\u6728\u3067\u50cd\u3044\u3066\u3044\u305f\u5143\u793e\u9577\u3067\u3059\u3002\u5df7\u3067\u306f\u30db\u30ea\u30a8\u30e2\u30f3\u3068\u3082\u3088\u3070\u308c\u3066\u3044\u307e\u3059\u3002 \u3042\u3001\u30e1\u30eb\u30de\u30ac http:\/\/goo.gl\/IMkx \u3067\u767b\u9332\u3067\u304d\u307e\u3059\u3002\u4ed5\u4e8b\u306e\u8a71\u3068\u304b\u306f info@takapon-jp.com \u3078\u3002","protected":false,"followers_count":957099,"friends_count":303,"listed_count":38090,"created_at":"Fri Jun 05 01:52:23 +0000 2009","favourites_count":2,"utc_offset":32400,"time_zone":"Tokyo","geo_enabled":true,"verified":true,"statuses_count":28191,"lang":"ja","contributors_enabled":false,"is_translator":false,"profile_background_color":"BADFCD","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme12\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme12\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/3440260453\/902666189d5b00c9371dd9e03f718e9c_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/3440260453\/902666189d5b00c9371dd9e03f718e9c_normal.jpeg","profile_link_color":"FF0000","profile_sidebar_border_color":"F2E195","profile_sidebar_fill_color":"FFF7CC","profile_text_color":"0C3E53","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"favorited":false,"retweeted":false,"lang":"ja"}
このままだと見づらいので、JSON整形サービスにかけて変換してみましょう。http://www.ctrlshift.net/jsonprettyprinter/
整形後のJSON
{ "contributors": null, "coordinates": null, "created_at": "Fri Apr 26 03:49:39 +0000 2013", "favorite_count": 0, "favorited": false, "geo": null, "id": 327630529061085184, "id_str": "327630529061085184", "in_reply_to_screen_name": null, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "lang": "ja", "place": null, "retweet_count": 0, "retweeted": false, "source": "<a href="http://www.echofon.com/" rel="nofollow">Echofon</a>", "text": "そこか! RT @Hiroki_Komazaki: 堀江貴文さんとレーシック繋がりだったとは!何か嬉しいw RT @kobe_kanagawa @takapon_jp ★堀江様と駒崎様は、レーシック繋がりですね♪今夜の朝生「各界で活躍する皆様の熱い討論」を楽しみにしています☆", "truncated": false, "user": { "contributors_enabled": false, "created_at": "Fri Jun 05 01:52:23 +0000 2009", "default_profile": false, "default_profile_image": false, "description": "六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。", "favourites_count": 2, "follow_request_sent": null, "followers_count": 957099, "following": null, "friends_count": 303, "geo_enabled": true, "id": 44791521, "id_str": "44791521", "is_translator": false, "lang": "ja", "listed_count": 38090, "location": "Tokyo", "name": "堀江貴文(Takafumi Horie)", "notifications": null, "profile_background_color": "BADFCD", "profile_background_image_url": "http://a0.twimg.com/images/themes/theme12/bg.gif", "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme12/bg.gif", "profile_background_tile": false, "profile_image_url": "http://a0.twimg.com/profile_images/3440260453/902666189d5b00c9371dd9e03f718e9c_normal.jpeg", "profile_image_url_https": "https://si0.twimg.com/profile_images/3440260453/902666189d5b00c9371dd9e03f718e9c_normal.jpeg", "profile_link_color": "FF0000", "profile_sidebar_border_color": "F2E195", "profile_sidebar_fill_color": "FFF7CC", "profile_text_color": "0C3E53", "profile_use_background_image": true, "protected": false, "screen_name": "takapon_jp", "statuses_count": 28191, "time_zone": "Tokyo", "url": "http://ameblo.jp/takapon-jp/", "utc_offset": 32400, "verified": true } }
textというキーの他にも、created_atとかuserとかがありますね。
注目すべきは、入れ子になっているキーです。この例だとuserを見てみましょう。
{ "user" : { "description" : "hogehoge...." } }
のようになっています。(JSONの値にJSONが入っている。マトリョーシカ的な。マトリョーシカ)
それではこのツイートを取得するコードを書いてみます。
このチュートリアルサイトのコードを参考にしました。Apache HttpClient - Tutorial
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONObject; @SuppressWarnings("deprecation") public class ParseJSON { public static void main(String args[]) { //StringBuilderを使って可変長の文字列を扱う //StringBuilderの使い方:http://www.javadrive.jp/start/stringbuilder/index1.html StringBuilder builder = new StringBuilder(); //HttpClientのインスタンスを作る(HTTPリクエストを送るために必要) HttpClient client = new DefaultHttpClient(); //HttpGetのインスタンスを作る(GETリクエストを送るために必要) HttpGet httpGet = new HttpGet( "https://api.twitter.com/1/statuses/user_timeline.json?screen_name=takapon_jp"); try { //リクエストしたリンクが存在するか確認するために、HTTPリクエストを送ってHTTPレスポンスを取得する HttpResponse response = client.execute(httpGet); //返却されたHTTPレスポンスの中のステータスコードを調べる // -> statusCodeが200だったらページが存在。404だったらNot found(ページが存在しない)。500はInternal server error。 int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 200) { //HTTPレスポンスが200よりページは存在する //レスポンスからHTTPエンティティ(実体)を生成 HttpEntity entity = response.getEntity(); //HTTPエンティティからコンテント(中身)を生成 InputStream content = entity.getContent(); //コンテントからInputStreamReaderを生成し、さらにBufferedReaderを作る //InputStreamReaderはテキストファイル(InputStream)を読み込む //BufferedReaderはテキストファイルを一行ずつ読み込む //(参考)http://www.tohoho-web.com/java/file.htm BufferedReader reader = new BufferedReader(new InputStreamReader(content)); String line; //readerからreadline()で行を読んで、builder文字列(StringBuilderクラス)に格納していく。 //※このプログラムの場合、lineは一行でなのでループは回っていない //※BufferedReaderを使うときは一般にこのように記述する。 while ((line = reader.readLine()) != null) { builder.append(line); } } else { System.out.println("Failed to download file"); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 文字列をJSONオブジェクトに変換する try { //JSON Arrayを作成する(文字列としてのJSONをJSON Arrayに変換) //builderはStringBuilderクラスなのでtoString()で文字列に変換 JSONArray jsonArray = new JSONArray(builder.toString()); //JSON Arrayのサイズを表示 System.out.println("Number of entries " + jsonArray.length()); //JSON Objectを作成する for (int i = 0; i < jsonArray.length(); i++) { //getJSONObjectでJSON Arrayに格納された要素をJSON Objectとして取得できる JSONObject jsonObject = jsonArray.getJSONObject(i); //JSON Objectをパースする //表示する際はgetString("ほげほげ")で"ほげほげ"をキーとする値を取得できる //userのように入れ子になっているときは、getJSONObject()を使って階層を下っていく System.out.println(i); System.out.println("投稿日:"+jsonObject.getString("created_at")); System.out.println("ツイート内容:"+jsonObject.getString("text")); System.out.println("ユーザー自己紹介:"+jsonObject.getJSONObject("user").getString("description")); System.out.println();//改行 } } catch (Exception e) { e.printStackTrace(); } } }
実行結果
Number of entries 10
0
投稿日:Fri Apr 26 03:51:48 +0000 2013
ツイート内容:私も。 RT @kenichiromogi: 死刑は国家による「殺人」です。私は死刑制度に反対します。Economist 最新号でも、死刑存続国は減少し続けているという分析記事が載っていました。 @asahi
【速報】法務省は26日朝、2人に死刑を執行しました。
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。1
投稿日:Fri Apr 26 03:49:39 +0000 2013
ツイート内容:そこか! RT @Hiroki_Komazaki: 堀江貴文さんとレーシック繋がりだったとは!何か嬉しいw RT @kobe_kanagawa @takapon_jp ★堀江様と駒崎様は、レーシック繋がりですね♪今夜の朝生「各界で活躍する皆様の熱い討論」を楽しみにしています☆
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。2
投稿日:Fri Apr 26 02:50:01 +0000 2013
ツイート内容:変わる。 やってみてください RT @afx2win: スタンプですか?収益じゃ無くて人気がある理由のつもりで聞いたのですがスタンプでそんなに変わるもの何ですか〜RT @takapon_jp: スタンプ RT @afx2win:
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。3
投稿日:Fri Apr 26 00:47:33 +0000 2013
ツイート内容:なるほど RT @RINDO_tales: ニコニコ超会議のチケットニコポイントで買えるのかー これで買うかな
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。4
投稿日:Fri Apr 26 00:47:12 +0000 2013
ツイート内容:10月丹波篠山 RT @odayoshio: 松茸〜
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。5
投稿日:Thu Apr 25 16:29:27 +0000 2013
ツイート内容:おお!マネージャーさん RT @Prison_Now: @takapon_jp 今日はありがとうございました。後ろがありましたのでご挨拶できず失礼いたしました。
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。6
投稿日:Thu Apr 25 15:53:53 +0000 2013
ツイート内容:おもろい RT @yjochi: 六本木ヒルズ10周年裏パーティー、をやるとおもしろいかも。表のパーティーでは呼ばれない人を集めて。ニコ生あたりでやればおもしろかった。
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。7
投稿日:Thu Apr 25 15:47:15 +0000 2013
ツイート内容:w RT @yjochi: しがない弁護士の自分が、六本木ヒルズ10周年のパーティーに呼ばれないのは当然だが、堀江氏は呼ばれてよかった気がするな。六ヒルの歴史の一部だから。
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。8
投稿日:Thu Apr 25 15:25:19 +0000 2013
ツイート内容:そんなに細かくないなあ RT @pottecasu: @takapon_jp 今まで既読機能を重視して褒めてるのかと思ってた…
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。9
投稿日:Thu Apr 25 15:16:43 +0000 2013
ツイート内容:スタンプ RT @afx2win: SkypeよりLINEの方が盛り上がってる理由って何なんでしょう?エンターテイメント性ですかね? RT @takapon_jp: でもそこがLINEの面白さの鍵だから問題意識以前に理解出来てないのは単なる馬鹿でしょ。
ユーザー自己紹介:六本木で働いていた元社長です。巷ではホリエモンともよばれています。 あ、メルマガ http://goo.gl/IMkx で登録できます。仕事の話とかは info@takapon-jp.com へ。