Hatena::ブログ(Diary)

cooldaemonの備忘録 RSSフィード

2010-01-22

SimpleHttpClient を用いた iPhone アプリのサンプルを公開します

http://github.com/cooldaemon/TestSimpleHttpClient

とても適当ですが、雰囲気は伝わるかと。

リクエスト送信、レスポンス受信と解析は、スレッドが異なるので、表示に干渉する場合は performSelectorOnMainThread:withObject:waitUntilDone: を利用する必要があります。

2009-09-12

SimpleHttpClient に HTML のフィルタを追加しました(HTML Document に対して XPath が使えます)

SimpleHttpClientKissXML+HTML を組み込んで HTML フィルタを作成しました。

下記のように、SimpleHttpClient のオブジェクトを作成し・・・

SimpleHttpClient *client = [[SimpleHttpClient alloc] initWithDelegate:self];

HTML 用のフィルタを設定し・・・

[client
    setFilter:SimpleHttpClientFilterHTML
      forHost:@"d.hatena.ne.jp"
];

リクエストを送ると・・・

[client
           get:@"http://d.hatena.ne.jp/cooldaemon/20090911/1252637257"
    parameters:nil
       context:nil
];

DDXMLDocument のオブジェクトが受け取れます。

- (void)simpleHttpClientOperationDidFinishLoading:(SimpleHttpClientOperation *)operation
                                     filteredData:(id)data
{
    [_html release];
    _html = (DDXMLDocument *)[data retain];
}

XPath を使うには、下記のようにします。

NSError *error = nil;
NSArray *body = [_html
    nodesForXPath:@"id(\"days\")//div[@class=\"body\"]//h3/following-sibling::*|id(\"days\")//div[@class=\"body\" and not(.//h3)]"
            error:&error
];

NSLog("%@", [body componentsJoinedByString:@""]);

詳しくは、test/TestHatenaDiaryHTML.m をご参照下さい。

2009-09-11

KissXML で HTML を扱えるように、KissXML+HTML を作りました

この話ですが、よくよく考えたら Objective-C にはカテゴリがあるので、Patch を作る必要はありませんでした。

Source Code と使い方は、こちら

HTML 対応とは全く無関係ですが、こっそり、xmlParseMemory を xmlReadMemory に入れ替えてます。

ついでに、使われていなかった option を利用して htmlReadMemory と xmlReadMemory のオプションを指定できるようにしました。

KissXML がバージョンアップすると利用できなくなる可能性もありますが、きっとすぐに対応できるハズ。

KissXML の開発チームに連絡してみよっと。

2009-09-10

SimpleHttpClient に JSON と XML のフィルタを追加しました

以前作った SimpleHttpClient ですが、ダウロード後のデータを BSJSONAdditionsKissXML に引き渡し、適宜オブジェクトを作成して返すようにしました。

WSSE に対応した際と同じくドメイン毎にフィルタを設定できるので、reader.livedoor.com から取得したデータは JSON フィルタを通す、b.hatena.ne.jp から取得したデータは XML フィルタを通す・・・という使い方が可能です。

具体的には、下記のように設定を行ないます。

SimpleHttpClient *client = [[[SimpleHttpClient alloc] initWithDelegate:self] autorelease];

[client
    setFilter:SimpleHttpClientFilterJSON
      forHost:@"reader.livedoor.com"
];

[client
    setFilter:SimpleHttpClientFilterXML
      forHost:@"b.hatena.ne.jp"
];

もし、http://reader.livedoor.com/api/subs に対してリクエストを送ったのであれば、フィルタ済みのデータを受け取るには下記のようにします。

- (void)simpleHttpClientOperationDidFinishLoading:(SimpleHttpClientOperation *)operation
                                     filteredData:(id)data
{
    _subs = (NSArray *)[data retain];
}

http://reader.livedoor.com/api/subs は、Array 型の JSON を返すので NSArray * にキャストします。

data は autorelease 済みであり、simpleHttpClientOperationDidFinishLoading:filteredData: メッセージは、サブスレッドの中で呼ばれるので、retain しておかないと、サブスレッド終了時に NSAutoreleasePool に release されてしまいます。

SimpleHttpClient 残作業

  • テストコードをリファクタリングする(テストコードに重複が多数あるため)
  • NSOperationQueue を外部から受け取るようにする(SimpleHttpClient 以外でもスレッドを使うため)
  • HTML 用のフィルタを作る(HTML でも XPath 使いたい!)

KissXML で HTML を無理矢理使う方法

KissXML で HTML を扱えるように、KissXML+HTML を作りました

そんなに KissXML に思い入れがあるわけではないのですが、HTML でも XPath を使いたいのと、XPathQuery と KissXML を一つのアプリで併用するのも嫌だったので、少しだけ KissXML の Source を追って HTML を使えるよう修正してみました。

ちなみに、この修正を行なうと XML が扱えなくなります。SimpleHttpClient の HTML フィルタを作成する際には、両方扱えるような修正を入れようかと思っています。その後に patch を作成する予定です。(patch を開発元に送るか検討中)

DDXMLDocument.h の頭で HTMLparser.h を import します。

#import <libxml/HTMLparser.h>

DDXMLDocument.m

- (id)initWithData:(NSData *)data options:(unsigned int)mask error:(NSError **)error
{
    //..snip..
    xmlDocPtr doc = xmlParseMemory([data bytes], [data length]);
    //..snip..
}

xmlParseMemory ではなく、htmlReadMemory を使用するように修正します。

xmlDocPtr doc = htmlReadMemory([data bytes], [data length], "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);

DDXMLNode.m

+ (BOOL)isXmlDocPtr:(xmlKindPtr)kindPtr
{
    return kindPtr->type == XML_DOCUMENT_NODE;
}

type が XML_HTML_DOCUMENT_NODE の時も真を返すようにします。

    return kindPtr->type == XML_DOCUMENT_NODE
        || kindPtr->type == XML_HTML_DOCUMENT_NODE;

たったこれだけで、KissXML が HTML を扱えるようになります。

ちなみに手元では、wedata にある LDRFullFeed を使って本文を抽出する事ができました。

2009-09-01

iPhone アプリ開発時、JSON ライブラリは BSJSONAdditions、json-framework、TouchJSON のうち、どれを使うべきか?

使い勝手や速度を論じる以前の問題で、BSJSONAdditions のみ下記の JSON を Parse 可能という残念な状態でした。

下記 JSON は、LDR の /api/subs の結果です。

[
  {
    "icon":"http://image.reader.livedoor.com/img/icon/default.gif",
    "link":"http://clip.livedoor.com/hot/",
    "subscribe_id":13060851,
    "unread_count":200,
    "folder":"ニュース",
    "tags":[],
    "rate":0,
    "modified_on":1251679561,
    "public":0,
    "title":"livedoor クリップ - 人気 ページ",
    "subscribers_count":40216,
    "feedlink":"http://clip.livedoor.com/rss/hot"
  },
  {
    "icon":"http://image.reader.livedoor.com/favicon/5/6/56e3a60d0bdcc14bf540bc25636c76decd2bffaf.png",
    "link":"http://blog.livedoor.com/article_ranking_index.html",
    "subscribe_id":13060852,
    "unread_count":200,
    "folder":"ニュース",
    "tags":[],
    "rate":0,
    "modified_on":1250464209,
    "public":0,
    "title":"livedoor Blog 記事別ランキング",
    "subscribers_count":39011,
    "feedlink":"http://blog.livedoor.com/xml/article_ranking.rdf"
  },
  {
    "icon":"http://image.reader.livedoor.com/favicon/1/1/1158357819388e9c26ed2a0fcff5867d092b8d54.png",
    "link":"http://blog.livedoor.jp/staff_reader/",
    "subscribe_id":13060853,
    "unread_count":1,
    "folder":"お知らせ",
    "tags":[],
    "rate":0,
    "modified_on":1248871104,
    "public":0,
    "title":"livedoor Reader 開発日誌",
    "subscribers_count":186610,
    "feedlink":"http://blog.livedoor.jp/staff_reader/atom.xml"
  },
  {
    "icon":"http://image.reader.livedoor.com/favicon/1/1/1158357819388e9c26ed2a0fcff5867d092b8d54.png",
    "link":"http://blog.livedoor.jp/feed_news/",
    "subscribe_id":13060854,
    "unread_count":21,
    "folder":"お知らせ",
    "tags":[],
    "rate":0,
    "modified_on":1251421218,
    "public":0,
    "title":"これはすごいブログ",
    "subscribers_count":89394,
    "feedlink":"http://blog.livedoor.jp/feed_news/atom.xml"
  }
]

json-framework

Parse 中にエラーとなりました。

バージョンを 0.2 まで落とすと Parse に成功しました。

TouchJSON

そもそも Root 要素が Array の JSON に対応していません。

Prefix として「{"array":」、Suffix として「}」を追加する事で Parse に成功しました。


テストコードは、現在手元に無いので、後で追記します。