【Android】XmlPullParser使い方まとめ
前回の記事では何の説明もなくXmlPullParserを使ってHTMLをパースしましたが、一応XmlPullParserそのものの使い方も説明しておこうと思います。英語が読めるならドキュメント読むだけで十分だとは思いますが…。
XMLをパースするためのものなので、当然XMLの仕様に関する知識がないとよくわからないところもあると思います。その辺は自分で調べてください。
XmlPullParserFactory
XmlPullParserにはファクトリクラスが用意されています。ここで事前に設定できるプロパティは以下の通りです。
結局のところ、これらはすべてFeatureを設定することになります。trueになってればオン、falseになっていればオフです。
じゃあFeatureって何なの?って話になるんですが、これはxmlpull.orgの資料を読まないとわかりません。
XmlPullParserに設定するFeature
と言うわけでその資料を読んでみましょう。
Feature | 意味 |
---|---|
FEATURE_PROCESS_NAMESPACES | XMLの名前空間に関する処理を使用する。 |
FEATURE_REPORT_NAMESPACE_ATTRIBUTES | 名前空間の修飾子(接頭語)を取得できるようになる。(FEATURE_PROCESS_NAMESPACESを設定しておくこと) |
FEATURE_PROCESS_DOCDECL | DOCTYPE Declarationを認識した際にイベントを発生させる。 |
FEATURE_VALIDATION | XML 1.0の仕様に適しているかを判断する。(反していた場合は例外が発生する) |
いずれのFeatureもデフォルト値はfalseです。
他にもFEATURE_XML_ROUNDTRIPが紹介されていますが、「サポートはするけどXMLPULL APIじゃないよ」と書かれているので説明は割愛します。
これらのFeatureはすべてXmlPullParserの定数値として宣言されています。FEATURE_XML_ROUNDTRIPは定数がないんですが、「http://xmlpull.org/v1/doc/features.html#xml-roundtrip 」をセットしてあげればいけるはずです。(試してはない)
また、前回HTMLをパースするためにFeatureにセットしたFEATURE_RELAXEDですが、これはandroid.util.Xmlの定数となっています。理由はkXML特有のFeatureだからです。どんな効果があるかはkXMLのトップページに書かれている内容の引用だけにとどめておきましょう。(と言うか、Android Developersのドキュメントにもこれ書いておけばいいのに。)
A robust “relaxed” mode for parsing HTML or SGML files (that are not well-formed XML documents) in order to avoid the need of two separate parsers in mobile phones.
兎にも角にも、XmlPullParserFactoryの各セッターは上記のFeatureをセットしてくれるメソッドでしかありません。XmlPullParserにもセッターは用意されていますが、既にStreamを渡している場合は基本的にセットできなくなるのでなるべく事前に準備しておきましょう。
必要なFeatureを全てセットし終えたらnewPullParserメソッドでインスタンスを作成します。
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); // 名前空間の使用 // FEATURE_PROCESS_NAMESPACESとFEATURE_REPORT_NAMESPACE_ATTRIBUTESが同時にセットされる? factory.setNamespaceAware(true); // XMLの検証(FEATURE_VALIDATION)を行わない factory.setValidating(false); XmlPullParser parser = factory.newPullParser();
XmlPullParserのイベント
それじゃあさっそくパースしていきましょう。
XmlPullParser#setInputにはReaderを受け取るオーバーロードと、InputStreamとエンコード指定を受け取るオーバーロードがあります。データソースがStringであればStringReaderあたりを使うのがベストではないでしょうか。
とりあえずまずはドキュメントに書いてあるコード例を読んでみましょう。
public static void main (String args[]) throws XmlPullParserException, IOException { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); XmlPullParser xpp = factory.newPullParser(); xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) ); int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if(eventType == XmlPullParser.START_DOCUMENT) { System.out.println("Start document"); } else if(eventType == XmlPullParser.START_TAG) { System.out.println("Start tag "+xpp.getName()); } else if(eventType == XmlPullParser.END_TAG) { System.out.println("End tag "+xpp.getName()); } else if(eventType == XmlPullParser.TEXT) { System.out.println("Text "+xpp.getText()); } eventType = xpp.next(); } System.out.println("End document"); }
何をするにしても、大体この形になります。getEventTypeで一番最初のイベントを取得し、そのイベントに応じて処理を実行し、nextで次のイベントを取得する。END_DOCUMENTイベントまで来たら終わりです。イベントはすべてintなので、switchにした方が読みやすいと思います。
勿論プルパーサの特性を生かして途中でreturnを書いておけば全文を読まなくてもパースが可能です。
あまりにも定型処理じみている割に毎回書くのがしんどいので、イベントハンドラを登録できる形でラップしてやりたくなるんですが、イベントの種類がそれなりにあるのでそれはそれでしんどいことは理解しておきましょう。
XmlPullParserで発生するイベントは以下の通りです。また、nextメソッドではなくnextTokenメソッドでのみ発生するイベントもあるので、取得したい内容で使い分けましょう。
イベント | 内容 | 取得メソッド |
---|---|---|
START_DOCUMENT | ドキュメントの開始 | 初回時のgetEventType |
START_TAG | タグの開始 | next / nextToken |
TEXT | タグのValue | next / nextToken |
CDSECT | CDATAセクション | nextToken |
COMMENT | コメント要素 | nextToken |
DOCDECL | DOCTYPE宣言セクション(see:FEATURE_PROCESS_DOCDECL) | nextToken |
IGNORABLE_WHITESPACE | インデントなどの空白 | nextToken |
ENTITY_REF | エンティティ参照 | nextToken |
END_TAG | タグの終了 | next / nextToken |
END_DOCUMENT | ドキュメントの終了 | next / nextToken |
要素の取得
後はもう適当なメソッドを使って欲しいものをとるだけです。
- getName - タグ名
- getNamespace - 名前空間のURI
- getPrefix - 名前空間の接頭語
- getText - タグの値
- getAttributeName - 属性名
- getAttributeValue - 属性の値
他にも色々ありますが、ドキュメントを読んだ方が早いです。
getAttributeValueはnamespace(URI、接頭語どちらでもOKっぽい)を指定しなきゃいけませんが、デフォルト名前空間でよければnullか空文字を渡しましょう。
また、大事なことですが、イベントの種類によっては取得できない値があります。まぁそりゃそうなんですけどね。START_TAGの時点ではTEXTの中身なんか知るわけないのでgetTextしたってとってこれません。逆に、TEXTイベントの時にタグ名や属性名を取得しようとしたってそんな昔のことはわかりません。DOMパーサじゃないんだもの。
それだけわかっていればもう大丈夫です。ガシガシパースしましょう。
まとめ
実例は前回の記事にはっつけたので、これで終わりです。