スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
このエントリーをはてなブックマークに追加

[iOS]libxml2を使いたくて四苦八苦

導入法と大雑把な利用法↓
[テン*シー*シー]
iPhoneアプリ開発、その(143) libxml2を使ってみる



このURLの説明だと導入までは簡単にできますが、ソース書いて即実行!とはいきません。
ということでここら辺の処理に踊らされたので、メモとして残しておきます。

下記実装は大体こういう感じの人向けです。
・NSXMLParserを使っていたんだけど、なんか遅いのでlibxml2に乗り換えたい。
・libxml2を使ってみたいけど、どんなのかわからない。
・とにかくコピペして、ログに吐かせて動きが見たい。

自分のソースの必要な部分へ必要なものをコピペするだけの簡易的なソースをつらつら書いていきます。

■注意■
この先の説明では書いてありませんが、
NSURLConnectionによるデータ取得を行っている必要があります。
またこのパースには非同期通信によるデータ取得が必然的に必要なため、
NSURLConnectionDelegateの実装を行ってください。
まだ行ってない方はとりあえずコチラを参考に実装を行ってみてください。
ヘッダ(.h)・メソッド(.m)ファイルに分割されておりませんが、似たように実装すればおのずと動くはずです。


①必要な変数を関数を記述
今回必要なものは、

・xmlSAXHandler
・xmlParserCtxtPtr
・startElementSAX
・endElementSAX
・charactersFoundSAX


の6つの実装です。
そのためにまずはヘッダファイルで宣言をしましょう。
以下ソースで書くとこんな感じです。

#import <libxml/tree.h>
@interface SampleLibXml{
xmlSAXHandler sax;
xmlParserCtxtPtr ctxt;
}

static void startElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI,
int nb_namespaces,
const xmlChar **namespaces,
int nb_attributes,
int nb_defaulted,
const xmlChar **atrributes);

static void endElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI);

static void charactersFoundSAX(void *context,
const xmlChar *characters,
int lenght);
@end


②変数の初期化
上記に挙げたうちの2つの変数、すなわち

xmlSAXHandler sax;
xmlParserCtxtPtr ctxt;

を初期化します。
書くところは初期化なので、
initか、initWithNibNameか、viewDidLoadかのいずれかでしょう。
自分の場合はviewDidLoadに追加しました。

ソースは以下のようになります。

-(void)viewDidLoad{
xmlDefaultSAXHandlerInit();
sax.characters = charactersFoundSAX;
sax.initialized = XML_SAX2_MAGIC;
sax.startElementNs = startElementSAX;
sax.endElementNs = endElementSAX;
ctxt = xmlCreatePushParserCtxt(&sax,self,NULL,0,NULL);
}


sax(xmlSAXHandler)への設定の部分についてですが、
「いや、俺は全部の設定がしてみたい!」
と考える方がいるかもしれないです。
そんな方はコチラをご覧になってください。
ソース中の67行目からが全構造体の記述の方法となっています。

それから、xmlCreatePushParserCtxtの引数についてですが、
第一引数はsaxの参照を、
第二引数はお好きなオブジェクトを、
(今回はselfをいれます。理由は、ココの"そのためにxmlCreatePushParserCtxtの..."あたりから読んでください)
第三~第五引数は空っぽです。ここは必要なら入れます。
ココに第三~第五引数だけ用いた例がありますので参考にしてください)

③connection:didReceiveDataメソッドでデータを即時パース
NSURLConnectionでアクセスすると、デリゲート関数がデータを受け付けるたびに呼ばれます。
そのデータを受け取る関数"connection:didReceiveData"の中で、受け取ったデータをxmlParseChunk()によってパースします。

以下がそのソースです。

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
xmlParseChunk(ctxt, (const char*)[data bytes], [data length], 0);
}


xmlParseChunk()についてですが、
第一引数には②手順で作成したctxtを、
第二引数にはパースする文字列
(ただし、CソースではNSData型なんてないのでchar型に変換してやらなければなりません)
第三引数にはパースする文字列の長さ
第四引数には終端指示子を入れます。
ココのサイトではfeof()関数を利用していますが、終端がないということで0を常に入れるという方法でもいいです)

④関数の実装
最後に、パース時通過するよう設定した3つの関数を実装します。
(設定っていうのは②の初期化でviewDidLoadに実装した部分です。)
では、startElementSAX・endElementSAX・charactersFoundSAXの3つを実装していきましょう。

以下がその実装となります。

static void startElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI,
int nb_namespaces,
const xmlChar **namespaces,
int nb_attributes,
int nb_defaulted,
const xmlChar **atrributes)
{
NSLog(@"startElementSAX()");
}

static void endElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI)
{
NSLog(@"endElementSAX()");
}


static void charactersFoundSAX(void *context,
const xmlChar *characters,
int lenght)
{
NSLog(@"charactersFoundSAX()");
}


これでパースした時の通り道が分かるようになります。
あとは煮るなり焼くなり好きにできます。
どうしてももうちょっと具体的に表示したい人のために、ちょっとだけ調理法のキッカケを置いておきます。
次のようにstartElementSAXを変更してみてください。

static void startElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI,
int nb_namespaces,
const xmlChar **namespaces,
int nb_attributes,
int nb_defaulted,
const xmlChar **atrributes)
{
NSLog(@"startElementSAX()\nlocalname:<%s>",localname);
}

どうですか、開始タグが表示されませんか?

○おまけのログだし参考URL
ログを出す時に、%sとしました。
あれはCソースでの出力方法です。
参考までに一覧化されているサイトを見つけておきました。
[WakabaProgrammingSchool]
フォーマット指定子一覧

ご一緒にNSLogの一覧もどうぞ。
[Cocoaリファレンス]
NSLog


⑤後始末をする
最後にctxtにデータが残ったままになりますので、こいつを空っぽにして後片づけしましょう。
また使うときにはctxtsaxをセットしてあげれば良いです。
ソースにすると以下のようになります。

-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//こちら正しい解放ではないようで、やたらとリークスを吐きだす
//xmlClearParserCtxt(ctxt);
//xmlCleanupParser();
//2011_08_23修正
//終わりを教える
xmlParseChunk(ctxt,NULL, 0, 1);
//解放
xmlFreeParserCtxt(ctxt);
}


これで完了となります。

蛇足かもしれないですが、
saxにセットする関数を減らすほど処理は速くなります。
適度に設定を行うことで場面ごとに最適なパースを行うことが可能になります。



このエントリーをはてなブックマークに追加

コメントの投稿

非公開コメント

Translation


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。
プロフィール

チーズくん

Author:チーズくん
個人的メモをただ羅列しています。
twitter:@cheese1038

バロメーター
最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。