【iPhone】KissXMLで快適パース生活

Posted by: daichi  /  Category: iphone開発

Web APIを使うiPhoneアプリにはXMLのパースは必要不可欠。アプリ開発者のみなさんはXMLのパースはどのようにしているのでしょう。NSXMLParser、libxml、TouchXML、KissXMLいろいろ方法はありますが、今回使ってみて結構使い勝手がよかったKissXMLの使い方を書いてみます。

KissXMLの導入

ダウンロード

まずはKissXMLをダウンロード。
ダウンロードはコンソールから下のコマンドをたたく。

svn checkout http://kissxml.googlecode.com/svn/trunk/ kissxml-read-only

プロジェクトで使えるようにする

ダウンロードが出来たらKissXML.xcodeprojをポチポチっと起動。
DDXMLグループの中身を全て自分のプロジェクトへコピー。

その後はプロジェクトのアクティブターゲットを編集から
* 他のリンカフラグ = -lxml2
* ヘッダ検索パス = /usr/include/libxml2
を設定すれば準備完了。

使い方

iPhoneでXMLをパースする機会と言うとほぼほぼWeb APIからのレスポンス解析だと思うので、そういう前提でいきます。ここではGoogle ReaderのAtomを解析してみます。
Google Readerからのレスポンスはhttp://www.google.com/reader/atom/をブラウザから叩くと確認できます。もちろんGoogleにログインしていないと見れません。

まず、XMLレスポンスを受け取ったらNSDataからDDXMLDocumentを作る。

?View Code OBJECTIVE-C
DDXMLDocument *doc = [[[DDXMLDocument alloc] initWithData:data options:0 error:&error] autorelease];

次にルートエレメントを取り出す。

?View Code OBUJECTIVE-C
DDXMLElement *root = [doc rootElement];

Google Readerからのレスポンスを見てみるとルートのfeedエレメントにはnamespaceをたくさん指定しています。こんな感じです。

<feed xmlns:idx="urn:atom-extension:indexing" xmlns:gr="http://www.google.com/schemas/reader/atom/" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom" idx:index="no">

なのでこれらをルートエレメントに教えてやる。

?View Code OBJECTIVE-C
[root addNamespace:[DDXMLNode namespaceWithName:@"idx" stringValue:@"urn:atom-extension:indexing"]];
[root addNamespace:[DDXMLNode namespaceWithName:@"gr" stringValue:@"http://www.google.com/schemas/reader/atom/"]];
[root addNamespace:[DDXMLNode namespaceWithName:@"media" stringValue:@"http://search.yahoo.com/mrss/"]];
[root addNamespace:[DDXMLNode namespaceWithName:@"foo" stringValue:@"http://www.w3.org/2005/Atom"]];

これで取り出す準備完了。defaultのnamespaceであるhttp://www.w3.org/2005/Atomには適当にfooという名前を付けた。

以下はGoogle ReaderからのAtomの一部抜粋。全部は長過ぎるため2件分だけ。

<?xml version="1.0"?>
 
<feed xmlns:idx="urn:atom-extension:indexing" xmlns:gr="http://www.google.com/schemas/reader/atom/" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom" idx:index="no">
<!--
Content-type: Preventing XSRF in IE.
-->
 
<generator uri="http://www.google.com/reader">Google Reader</generator>
<id>tag:google.com,2005:reader/user/10689165820831989733/state/com.google/reading-list</id>
<title>Daichi の Google リーダー購読リスト</title>
<gr:continuation>CJzIrMuvmZwC</gr:continuation>
<link rel="self" href="http://www.google.com/reader/atom/"/>
<author><name>Daichi</name></author>
<updated>2009-08-10T16:06:18Z</updated>
<entry gr:crawl-timestamp-msec="1249920378537">
	<id gr:original-id="http://www.itmedia.co.jp/news/articles/0908/10/news045.html">tag:google.com,2005:reader/item/7b14ce7e8094cf36</id>
	<category term="user/10689165820831989733/label/はてな" scheme="http://www.google.com/reader/" label="はてな"/>
	<category term="user/10689165820831989733/state/com.google/reading-list" scheme="http://www.google.com/reader/" label="reading-list"/>
	<category term="user/10689165820831989733/state/com.google/fresh" scheme="http://www.google.com/reader/" label="fresh"/>
	<category term="スポーツ・芸能・音楽"/>
	<title type="html">ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia News</title>
	<published>2009-08-10T06:10:05Z</published>
	<updated>2009-08-10T06:10:05Z</updated>
	<link rel="alternate" href="http://www.itmedia.co.jp/news/articles/0908/10/news045.html" type="text/html"/>
	<content xml:base="http://b.hatena.ne.jp/hotentry" type="html">&lt;blockquote title="ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia News"&gt;&lt;cite&gt;&lt;img
src="http://favicon.st-hatena.com/?url=http0X1.30E20927D8A28P-10090.0000000.000000www.itmedia.co.jp0.000000news-0.000000articles0.0000000908-0.000000100.000000news045.html" alt=""&gt; &lt;a
href="http://www.itmedia.co.jp/news/articles/0908/10/news045.html"&gt;ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia News&lt;/a&gt;&lt;/cite&gt;&lt;p&gt;&lt;a href="http://www.itmedia.co.jp/news/articles/0908/10/news045.html"&gt;&lt;img
src="http://img.b.hatena.ne.jp/entryimage/15253758-1249897906.jpg" alt="ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia News" title="ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia
News"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;ニュースねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に覚醒剤取締法違反の疑いで逮捕された酒井法子容疑者の楽曲の「iTunes Store」販売ランキングが急上昇している。2009年08月10日 14時45分 更新iTunes Storeのアルバムランキング覚醒剤取締法違反の疑いで8月8日に逮捕された酒井法子容疑者の楽曲が、「iTunes
Store」販売ラン...&lt;/p&gt;&lt;p&gt;&lt;a href="http://b.hatena.ne.jp/entry/http://www.itmedia.co.jp/news/articles/0908/10/news045.html"&gt;&lt;img
src="http://b.hatena.ne.jp/entry/image/http://www.itmedia.co.jp/news/articles/0908/10/news045.html" alt="はてなブックマーク - ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia News" title="はてなブックマーク - ねとらぼ:酒井法子容疑者の「碧いうさぎ」、iTunes Storeで2位に - ITmedia
News" border="0" style="border:none"&gt;&lt;/a&gt; &lt;a href="http://b.hatena.ne.jp/append?http://www.itmedia.co.jp/news/articles/0908/10/news045.html"&gt;&lt;img src="http://b.hatena.ne.jp/images/append.gif" border="0" alt="はてなブックマークに追加"
title="はてなブックマークに追加"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://feeds.feedburner.com/~r/hatena/b/hotentry/~4/LMheVEu9xoE" height="1" width="1"&gt;
	</content>
	<author gr:unknown-author="true">
		<name>(投稿者不明)</name>
	</author>
	<source gr:stream-id="feed/http://feedproxy.google.com/hatena/b/hotentry">
		<id>tag:google.com,2005:reader/feed/http://feedproxy.google.com/hatena/b/hotentry</id>
		<title type="html">はてなブックマーク - 人気エントリー</title>
		<link rel="alternate" href="http://b.hatena.ne.jp/hotentry" type="text/html"/>
	</source>
</entry>
 
<entry gr:crawl-timestamp-msec="1249919130170">
	<id gr:original-id="http://kengo.preston-net.com/archives/004237.shtml">
		tag:google.com,2005:reader/item/e781184cc384d80b
	</id>
	<category term="user/10689165820831989733/label/はてな" scheme="http://www.google.com/reader/" label="はてな"/>
	<category term="user/10689165820831989733/state/com.google/reading-list" scheme="http://www.google.com/reader/" label="reading-list"/>
	<category term="user/10689165820831989733/state/com.google/fresh" scheme="http://www.google.com/reader/" label="fresh"/>
	<category term="コンピュータ・IT"/>
	<title type="html">Going My Way: iPhone のカメラでズーム機能を実現するアプリ、Camera Zoom</title>
	<published>2009-08-10T14:46:02Z</published>
	<updated>2009-08-10T14:46:02Z</updated>
	<link rel="alternate" href="http://kengo.preston-net.com/archives/004237.shtml" type="text/html"/>
	<content xml:base="http://b.hatena.ne.jp/t/iphone?sort=eid" type="html">&lt;blockquote title="Going My Way: iPhone のカメラでズーム機能を実現するアプリ、Camera
Zoom"&gt;&lt;cite&gt;&lt;img src="http://favicon.st-hatena.com/?url=http-0X1.FD6B8P+00.000000-0.000000kengo.preston-net.com0.000000archives-0.000000004237.shtml" alt=""&gt; &lt;a
href="http://kengo.preston-net.com/archives/004237.shtml"&gt;Going My Way: iPhone のカメラでズーム機能を実現するアプリ、Camera Zoom&lt;/a&gt;&lt;/cite&gt;&lt;p&gt;iPhone のカメラでズーム機能を実現するアプリ、Camera Zoomスポンサード リンクiPhone
のカメラにはズーム機能などがありません。なので遠くの物を撮る際は小さくなってしまいます。近づける場合は近づいて撮ればいいのですがなかなかそうもいかない場合もあります。そんな場合に、ズーム機能をアプリでカバーしてくれる、Camera Zoom というアプリがあるのでこれを入れておく...&lt;/p&gt;&lt;p&gt;&lt;a
href="http://b.hatena.ne.jp/entry/http://kengo.preston-net.com/archives/004237.shtml"&gt;&lt;img src="http://b.hatena.ne.jp/entry/image/http://kengo.preston-net.com/archives/004237.shtml" alt="はてなブックマーク - Going My Way: iPhone
のカメラでズーム機能を実現するアプリ、Camera Zoom" title="はてなブックマーク - Going My Way: iPhone のカメラでズーム機能を実現するアプリ、Camera Zoom" border="0" style="border:none"&gt;&lt;/a&gt; &lt;a
href="http://b.hatena.ne.jp/append?http://kengo.preston-net.com/archives/004237.shtml"&gt;&lt;img src="http://b.hatena.ne.jp/images/append.gif" border="0" alt="はてなブックマークに追加"
title="はてなブックマークに追加"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
	</content>
	<author gr:unknown-author="true">
		<name>(投稿者不明)</name>
	</author>
	<source gr:stream-id="feed/http://b.hatena.ne.jp/t/iphone?sort=eid&amp;mode=rss">
		<id>tag:google.com,2005:reader/feed/http://b.hatena.ne.jp/t/iphone?sort=eid&amp;mode=rss</id>
		<title type="html">はてなブックマーク - タグ - iphone</title>
		<link rel="alternate" href="http://b.hatena.ne.jp/t/iphone?sort=eid" type="text/html"/>
	</source>
</entry>
</feed>

この中からフィードのタイトル、フィードのID、エントリーのタイトル、エントリー本文、公開日を抜き取ってみる。

?View Code OBJECTIVE-C
 NSArray *feedTitles = [root nodesForXPath:@"//foo:source/foo:title" error:&error];
 NSArray *feedIds = [root nodesForXPath:@"//foo:source/foo:id" error:&error];
 NSArray *entryTitles = [root nodesForXPath:@"//foo:entry/foo:title" error:&error];
 NSArray *contents = [root nodesForXPath:@"//foo:entry/foo:content" error:&error];
 NSArray *publisheds = [root nodesForXPath:@"//foo:entry/foo:published" error:&error];

これでそれぞれの配列で取得できる。
あとはこれをループしてオブジェクトにまとめる。

?View Code OBJECTIVE-C
    for (NSUInteger i = 0; i < [feedTitles count]; i++) {
		NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease];
		[result setObject:[[feedIds objectAtIndex:i] stringValue] forKey:@"feed_id"];
		[result setObject:[[feedTitles objectAtIndex:i] stringValue] forKey:@"feed_title"];
		[result setObject:[[entryTitles objectAtIndex:i] stringValue] forKey:@"entry_title"];	
		[result setObject:[[contents objectAtIndex:i] stringValue] forKey:@"content"];
		[result setObject:[[publisheds objectAtIndex:i] stringValue] forKey:@"published"];		
                [self.objects addObject:result];
    }

これまでの処理をまとめて書くとこんな感じ

?View Code OBJECTIVE-C
    DDXMLDocument *doc = [[[DDXMLDocument alloc] initWithData:data options:0 error:&error] autorelease];
 
    DDXMLElement *root = [doc rootElement];
    [root addNamespace:[DDXMLNode namespaceWithName:@"idx" stringValue:@"urn:atom-extension:indexing"]];
    [root addNamespace:[DDXMLNode namespaceWithName:@"gr" stringValue:@"http://www.google.com/schemas/reader/atom/"]];
    [root addNamespace:[DDXMLNode namespaceWithName:@"media" stringValue:@"http://search.yahoo.com/mrss/"]];
    [root addNamespace:[DDXMLNode namespaceWithName:@"foo" stringValue:@"http://www.w3.org/2005/Atom"]];
 
    NSArray *titles = [root nodesForXPath:@"//foo:feed/foo:title" error:&error];
    NSArray *feedTitles = [root nodesForXPath:@"//foo:source/foo:title" error:&error];
    NSArray *feedIds = [root nodesForXPath:@"//foo:source/foo:id" error:&error];
    NSArray *entryTitles = [root nodesForXPath:@"//foo:entry/foo:title" error:&error];
    NSArray *contents = [root nodesForXPath:@"//foo:entry/foo:content" error:&error];
    NSArray *publisheds = [root nodesForXPath:@"//foo:entry/foo:published" error:&error];
 
    for (NSUInteger i = 0; i < [feedTitles count]; i++) {
	NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease];
	[result setObject:[[feedIds objectAtIndex:i] stringValue] forKey:@"feed_id"];
	[result setObject:[[feedTitles objectAtIndex:i] stringValue] forKey:@"feed_title"];
	[result setObject:[[entryTitles objectAtIndex:i] stringValue] forKey:@"entry_title"];	
	[result setObject:[[contents objectAtIndex:i] stringValue] forKey:@"content"];
	[result setObject:[[publisheds objectAtIndex:i] stringValue] forKey:@"published"];		
        [self.objects addObject:result];
    }


NSXMLParserやTouchXMLなどよりもなんだかすっきりできる気がする。
検証はしていないのだけど噂ではTouchXMLよりも高速らしい。
名前もいいし一度お試しあれ。

タグ: API, iphone, KissXML, NSArray, objecti, objective-c, Xcode, XML, はてなブックマーク, アプリ, カメラ, ダウンロード, ライブラリ, リスト,

関連する投稿

コメント

Additional comments powered by BackType

Get Adobe Flash playerPlugin by wpburn.com wordpress themes