せでぃのブログ

ブログ初心者おいどんのどうでもいい愚痴やどうでもいい愚痴やどうでもいいマメ知識などを披露するチラシの裏です。

FESSとJSONその1

20150305追記:2パターン作ってみたの項目を作成
 JSONを使ってみたいがために、右も左もわからない内に他人のサーバであれこれやるのはよろしくないので、FESSをひさびさに起動してみた。クライアント側のJSONの取得にはこの辺を参考にというかもろパクった。

Objective-CでJSON取得して表示する方法 - Qiita

 test1として、プロジェクトを上げた。
csny/fess-json · GitHub
出力結果がこれ。

完成画像

f:id:Sediment:20150301011752p:plain
以下解説。

何のためにJSONを使うの?

 あちこちのwebサーバで公開されているAPIから情報を取得して、自分なりにデータを整理しなおして見やすく表示するソフト・アプリを作るため。当然、クライアントとサーバはHTTPで会話する。
 使用目的としては、XMLの代わりと考えて良さげ。親切なところだと、JSONXMLのどちらでもデータを公開しているので、何が違うかとかは自分で調べて欲しい。流行ってるから使ってみるんだ、この野郎。


 例えば、東電の消費電力、お天気情報、Twitterなどの公開されているデータを取得するといった使い方がわかりやすいかも。

サーバ側はどうするの?

 幸いなことにFESSサーバの場合、デフォルトで以下のようにJSON取得のアクセス方法が用意されていたので、知らない。
f:id:Sediment:20150228173634p:plain

JSONはどうやって取得するの?

 大抵は、JSONの取得方法は各サーバで公開され具体的に説明されている。FESSの場合のJSON取得アドレスとオプション、レスポンスの仕様は以下の通り。
http://fess.codelibs.org/ja/9.2/user/json-response.html
 何もせずに出力すると情報が多くて訳がわからんので(と言ってもパースしてしまえばインデントされてるので慣れると結構みやすい)、webアクセスによる出力結果と比較して必要な情報だけ出力するものを考える。
f:id:Sediment:20150228173618p:plain
 何もしないで受け取ったJSONの配列を表示するとこういう結果が返ってくる。わからん。

<7b227265 73706f6e 7365223a 7b227665 7273696f 6e223a39 
2e322c22 73746174 7573223a 302c2271 75657279 223a2274 
7874222c 22657865 6354696d 65223a32 2e36312c 22717565

 パースすれば次のように読めるようになるけど、情報量が多くて読みづらい。英語ではparseらしいので、解剖とか解析といった意味か。

{
    response =     {
        execTime = "0.5";
        pageCount = 5;
        pageNumber = 1;
        pageSize = 20;
        query = txt;
        queryTime = 435;
        recordCount = 90;
        result =         (
                        {
                boost = 1;
                contentDescription = " replacement functionality. === === 
UPGRADE-1.2.<em>txt</em> -- Upgrade info for 1.0 to 1.2 === 
UPGRADE-1.4.<em>txt</em>... -- Upgrade info for 1.2 to 1.4 === 
UPGRADE-1.6.<em>txt</em> -- Upgrade info for 1.4 to 1.6 === 
UPGRA...";
                contentLength = 3871;
                created = "2014-08-19T01:41:44.287+0900";
                digest = "...==================================
========================= === === Information for 
upgrading between Asterisk versions === === These files document 
all the changes that MUST be taken === into acco...";
                docId = 822b158e296543afbb2b260c02aa0670;
                "filetype_s" = others;
                host = localhost;
                id = "file:/opt/asterisk-11.10.0/UPGRADE-10.txt";
                lastModified = "2012-07-16T23:02:10.000+0900";
                mimetype = "text/plain";
                score = "1.3712595";
                site = "/opt/asterisk-11.10.0/UPGRADE-10.txt";
                title = "UPGRADE-10.txt";
                url = "file:/opt/asterisk-11.10.0/UPGRADE-10.txt";
                urlLink = "file://opt/asterisk-11.10.0/UPGRADE-10.txt";
            },
                        {
                boost = 1;
                contentDescription = ". === === UPGRADE-1.2.<em>txt
</em> -- Upgrade info for 1.0 to 1.2 === UPGRADE-1.4.<em>txt
</em> -- Upgrade info for 1.2 to 1.4.../manager_1_1.<em>txt</em> 
for information * The IAXpeers command output has been changed to more...";
                contentLength = 13965;
                created = "2014-08-19T02:31:44.217+0900";
                digest = "...==================================
======================= === === Information for upgrading 
from Asterisk 1.4 to 1.6 === === These files document all the 
changes that MUST be taken === into account...";
                docId = 888e3c642bcc481d94532a686ca7a53c;
                "filetype_s" = others;
                host = localhost;
                id = "file:/opt/asterisk-11.10.0/UPGRADE-1.6.txt";
                lastModified = "2010-05-08T01:05:24.000+0900";
                mimetype = "text/plain";
                score = "1.3296582";
                site = "/opt/asterisk-11.10.0/UPGRADE-1.6.txt";
                title = "UPGRADE-1.6.txt";
                url = "file:/opt/asterisk-11.10.0/UPGRADE-1.6.txt";
                urlLink = "file://opt/asterisk-11.10.0/UPGRADE-1.6.txt";
            },


〜以下、18個繰り返し

 これでは読みづらいので、"title","url","lastModified"の3項目を抽出して表示するプログラムにしてみる。

2パターン作ってみた

 JSONのパース部分と出力部分で、NSDictionaryを使うかvalueForKeyPathを使うかというパターンが大きく2つ考えられる。比較したりほかのコードとの使い回しがしやすいように両方書いてみた。
パターン1.NSArray-valueForKeyPath

    // JSONをパース、パターン1 (NSArray-valueForKeyPath)
    NSArray *array = [NSJSONSerialization JSONObjectWithData:json options:NSJSONReadingAllowFragments error:nil];
    // 要素数取得
    NSString *temp_cnt = [array valueForKeyPath:@"response.pageSize"];
    const int cnt = [temp_cnt intValue]; // strからint型に変換


    // パターン1 (NSArray-valueForKeyPath)
    // JSONの配列からi番目のtitle,url,lastmodifiedを抽出して出力
    // 階層(入れ子)構造になっていれば"."で区切って、全階層を記述する
    // 1まとまりずつ出力したいため、objectAtIndex:iというのを入れている
    for (int i=0;i<cnt;i++){
        NSLog(@"ファイル名:%@ URL:%@ 最終更新:%@", [[array valueForKeyPath:@"response.result.title"] objectAtIndex:i], [[array valueForKeyPath:@"response.result.url"] objectAtIndex:i], [[array valueForKeyPath:@"response.result.lastModified"] objectAtIndex:i]);
    }

パターン2.NSDictionary-objectForKey

     // JSONをパース、パターン2 (NSDictionary-objectForKey)
     NSDictionary *temp_array = [NSJSONSerialization JSONObjectWithData:json options:0 error:nil];
     // NSDictionaryにセットされた値からまずresponseを取り出してtemp_arrayに再セット
     temp_array = [temp_array objectForKey:@"response"];
     // さらにresultを取り出してtemp_arrayに再セット
     NSDictionary *array = [temp_array objectForKey:@"result"];
     // 要素数取得
     const unsigned long cnt = [array count];


     // パターン2 (NSDictionary-objectForKey)
     // JSONの配列からtitle,url,lastmodifiedを抽出して出力
     for (NSDictionary *out_array in array) {
     NSLog(@"ファイル名:%@ URL:%@ 最終更新:%@", [out_array objectForKey:@"title"], [out_array objectForKey:@"url"], [out_array objectForKey:@"lastModified"]);
     }

「お前は、JSON言いたいだけ、ちゃうんかと。」
「うむ。予定通りプランB*1だ。」

*1:別名ノープランとも