読者です 読者をやめる 読者になる 読者になる

【Android】XmlPullParser使い方まとめ

Android

前回の記事では何の説明もなく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

要素の取得

後はもう適当なメソッドを使って欲しいものをとるだけです。

他にも色々ありますが、ドキュメントを読んだ方が早いです。

getAttributeValueはnamespace(URI、接頭語どちらでもOKっぽい)を指定しなきゃいけませんが、デフォルト名前空間でよければnullか空文字を渡しましょう。

また、大事なことですが、イベントの種類によっては取得できない値があります。まぁそりゃそうなんですけどね。START_TAGの時点ではTEXTの中身なんか知るわけないのでgetTextしたってとってこれません。逆に、TEXTイベントの時にタグ名や属性名を取得しようとしたってそんな昔のことはわかりません。DOMパーサじゃないんだもの。

それだけわかっていればもう大丈夫です。ガシガシパースしましょう。

まとめ

実例は前回の記事にはっつけたので、これで終わりです。

参考