お使いのブラウザは、バージョンが古すぎます。

このサイトは、Internet Explore8・Internet Explore9には対応しておりません。
恐れ入りますが、お使いのブラウザをバージョンアップしていただきますよう宜しくお願いいたします。

【Android】 SAXParserのcharactersメソッドで、テキストがきちんと取得できない時の対処法

こんにちわ。pencoです。
本日はXMLのパース処理を作成していたら、なんだかパース結果がおかしいぞ!となった時の事を書いていきたいと思います。
 
 
表題にもありますがXMLのパースにはSAXParser を使用しています。
SAXParser の使い方についてはまた別の機会に。。。
ざっくり、メソッドの説明だけ。

  • startDocument, endDocument
  • ドキュメントの始まり、ドキュメントの最後発生するイベント

  • startElement, endElement
  • 開始タグ、終了タグを読み込んだ際に発生するイベント

  • characters
  • 要素内の文字データを読み込んだ際に発生するイベント

 

private class TestHandler extends DefaultHandler {
    public String value;

    @Override
    public void startDocument() throws SAXException {
    	super.startDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName,
    Attributes attributes) throws SAXException {
    	super.startElement(uri, localName, qName, attributes);
        //開始タグでの処理。
        //アトリビュートの処理はここで行う。		
    }

    @Override
    public void characters(char[] ch, int start, int length)
    throws SAXException {
    	super.characters(ch, start, length);
        //テキストデータの取得
    	value = new String(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName)
    throws SAXException {
    	super.endElement(uri, localName, qName);
    	//終了タグでの処理
        //取得したvalueを保存、などなど…
    }

    @Override
    public void endDocument() throws SAXException {
    	super.endDocument();
        //ドキュメント終了時の処理。コールバックの呼び出しなど
    }
}

テキストが一部取得されない

 
こんな感じでcharacters メソッドでテキストデータを取得していたのですが、あるいくつかのタグのテキストデータだけが、途中から取得されていることに気付きました。
例をあげると、

<description>ジェームズ・アレン 1864年生まれ イギリスの作家 代表作であるAs a Man Thinketh(邦訳,『「原因」と「結果」の法則』)は自己啓発書の原点であり、デール・カーネギー、アール・ナイチンゲールなどに大きな影響を与え、現在も世界中で売れ続けている。聖書につぐロングセラーの一つと言われており、日本でも50万部を超えるベストセラーである。</description>

というタグのパース結果が

value = "、アール・ナイチンゲールなどに大きな影響を与え、現在も世界中で売れ続けている。聖書につぐロングセラーの一つと言われており、日本でも50万部を超えるベストセラーである。"

という様に、後ろ半分しか取得できてない状態です。
 
まず浮かんだのが、文字数が多すぎるのではないか?という点。
しかし、これより文字数の多いタグはきちんと取得できているし、何より、問題発生位置より後ろにある同じ内容のタグは取得できているんです。
 

対処方法

 
色々ログを出してみた結果、characters メソッドは1つのタグにつき1回しか呼ばれないというわけではなくて、複数回呼ばれるということが判りました。
 
だいたいは、1回目にしか引数が入ってこないので、1回目の呼び出しで取得されたvalue は上書きされずに、endElement メソッドにたどり着きます。
しかし今回の例では、
 
  1回目の呼び出しで引数 length が91
  2回目の呼び出しで引数 length が83
 
という様に呼ばれたため、1回目の呼び出しで取得したvalue(前半の91文字)が、2回目の呼び出しで取得したvalue(後半の83文字)で上書きされてしまったため、endElement メソッドでvalue を処理する段階では後ろ半分の値が返ってきた、というわけです。
 
 
この問題に対処するため、以下の様に変更しました。

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
    super.startElement(uri, localName, qName, attributes);
    flag = true;
    vCount = 0;		
}

@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
    super.characters(ch, start, length);
    		
    String v1 = "";
    if(flag){
    	v1 = value;
    }
    		
    value = new String(ch, start, length);
	
    if(flag && vCount > 0){
    	value = v1 + value;
    }
    vCount++;
}

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
    super.endElement(uri, localName, qName);
    flag = false;
}

 
まず、開始タグ~終了タグの間に呼ばれたcharacters メソッドでのみ処理を行うようにするための flag を作ります。このflag が立っている間は、前に取得したvalue の値を控えておきます。
次に、characters メソッドが何回呼ばれたかをカウントする変数vCount を作ります。
 
この二つを使い、開始タグ~終了タグの間characters メソッドが複数回呼ばれた場合は、前に取得したvalueと連結したvalue を生成するというようにしました。
 
これで、charactersメソッドが複数回呼ばれた場合にvalueが分割されてしまう問題に対処することが出来ました。
 
 
今回はこの辺で。。

コメントをどうぞ

メールアドレスは公開されません。* が付いている欄は必須項目です。


お気軽にお問い合わせください。

日本VTR実験室では、お仕事のご依頼、ブログ・コラムのご感想などを受け付けております。
アプリ開発・コンテンツ制作でお困りでしたら、お気軽にご相談ください。
ご連絡お待ちしております。

お問い合わせはこちらから

03-3541-1230

info@nvtrlab.jp

電話受付対応時間:平日AM9:30〜PM6:00