今回は残りの、提案、支持ページの解析。
 なのでhtmlParserViewControllerクラスに実装してたNSXMLParserDelegateメソッド群と関連するインスタンス変数群を抽出して別クラスを定義するっす。
 名前はkonohanaParserにします。

$テン*シー*シー-1

 でまあ、別クラスになった以上、解析結果を取り出せるようにしないと駄目なわけで…
 今回はNSMutableArrayクラスとNSMutableDictionaryクラスを使う事にしました。

$テン*シー*シー-2

 NSMutableDictionaryはキーとなる文字列と対応するオブジェクトのペアで管理するクラスで、すでに
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {

 メソッドで使ったことのあるNSDictionaryクラスの変更可能版です。上記メソッドでは
NSString* className = [attributeDict valueForKey:@"class"];

 てな感じで、キー文字列(ここでは@"class")に対応するオブジェクトを取り出してたのを覚えてるかな?
 これで、例えば投稿なら、各投稿項目のIDやコメント、写真URLを
NSMutableDictionary* dic = [[NSMutableDictionary alloc]init];

 で作成したNSMutableDictionaryインスタンスに
[dic setValue:IDのNSNumberインスタンス forKey:@"ID"];
[dic setValue:URLのNSStringインスタンス forKey:@"url"];

 てな感じで登録してやって、このNSMutableDictionaryインスタンスの配列をNSMutableArrayで管理しようちゅーわけです。
 NSNumberクラスはその(207)で説明したとおり数字のオブジェクト。
 NSMutableArrayやNSMutableDictionary、キー値コーディングとか結構使う機会がある。

 あとは、このkonohanaParserクラスを親クラスとして、投稿用、提案用、支持用解析クラスを作るわけです。

$テン*シー*シー-3

 さっしはついてると思うけど以下のメソッドを投稿用、提案用、支持用解析クラス別に実装するわけですな。解析自体は前回の応用なんで特に説明しません。興味ある人はソース読んで下さい。
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName;

 今回は各クラスを公開せずにkonohanaParserクラスだけ公開して、指定された解析クラスを返すクラスメソッドを用意しますた。
enum {
parse_contribute,
parse_suggestion,
parse_vote
};
+(konohanaParser*)parserWithType:(int)type {
if (type == parse_contribute)
return [[[konohanaParserForContribute alloc] init] autorelease];
if (type == parse_suggestion)
return [[[konohanaParserForSuggestion alloc] init] autorelease];
return [[[konohanaParserForVote alloc] init] autorelease];
}

 でもって解析用のメソッド
// urlで指定されたXMLを解析する。
-(void)parse:(NSString*)url;

 だけ公開。

 使う側はこんな感じで使うわけっす。
konohanaParser* parser =
[konohanaParser parserWithType:解析対象用クラスの指定];

 とやって解析用インスタンスを作り
[parser parse:URLパス];

 で解析。結果を
parser.list

 から取り出す。
 こうやるとkonohanaParserForContributeやkonohanaParserForSuggestion、konohanaParserForVoteの変更によって使う側は再コンパイルがかかることがないんですな。
 まあ、このプロジェクトの規模ならオーバースペックだけどね。大規模開発だとここらへんの工夫が結構有効だったりします。

 ちゅーことで実装終わったら解析だ~と試しに提案ページを解析させると、いきなり途中で解析が終わってしまいますた。
 なんでかな~とparser:parseErrorOccurred:メソッドでエラー内容を表示させると
error = Error 23, Description: Operation could not be completed. (NSXMLParserErrorDomain error 23.), Line: 16, Column: 60

 とか出るんですわ。
 エラー23は & が出てきたのに ; で終了してないよってエラーで、例えば
<

 てのはXMLのタグの開始文字として使われてるので、そのまま内容部に書くわけにいかないんですな。そのため
&lt;

 なんてふうに書く必要があります。
 今回はリンクURLとして埋め込まれた
<a class="contents" href = "suggest.php?contribution_id=12&suggestion_id=43">

 がひっかかったわけです。
<a class="contents" href = "suggest.php?contribution_id=12&amp;suggestion_id=43">

 と書かないと駄目なのね。

 しょうがないので、PHP側のソースを修正しました。
 厳格にも程度ってもんが…

 にしてもSafariは元の記述でも平気でページを表示してたわけで、libxml2ライブラリ側にはHTMLparser: なんてAPIがあるので、ここらへんを使えば、この問題は回避できるのかもね。

 ま、とりあえず各解析結果を
NSLog(@"%@\n", parser_contribute.list);
NSLog(@"%@\n", parser_suggestion.list);
NSLog(@"%@\n", parser_vote.list);

 でコンソールに出力して無事解析できてることを確認。前にも説明したけどNSLogは%@を使うとそのオブジェクトの説明が表示される。NSArray継承クラスだと格納してるオブジェクトの一覧。でもってそいつがNSDictonary継承クラスだと、やっぱり格納してるキーとオブジェクトのペアの一覧を出してくれる。漢字を含む文字列は内部表現のままなんで読みにくいけど、かなり便利っす。

2010-07-08 02:47:58.695 htmlParser[5813:207] (
{
ID = 13;
comment = "";
name = f;
url = "./img_xcc_test/20100516084849.JPG";
},



{
ID = 8;
comment = "\U9ec4\U8272\U3044\U82b1";
name = "\U305f\U3093\U307d\U307d";
url = "./img_xcc_test/20100516084800.JPG";
}
)
2010-07-08 02:47:58.698 htmlParser[5813:207] (
{
ID = 43;
name = 1;
vote = 5;
},



{
ID = 51;
name = f1;
vote = 1;
}
)
2010-07-08 02:47:58.699 htmlParser[5813:207] (
{
ID = test2;
},
{
ID = test;
}
)


------------
サンプルプロジェクト:htmlParser04.zip
PHP側ソース:htmlParser04-PHPSource.zip

SDK 4を使ってる人はプロジェクト>プロジェクト設定を編集でベースSDKをiPhone デバイス3.2か4.0にしてちょ。

$テン*シー*シー-5