SAXParserをXmlPullParserにて書き換えてみた

普通のプログラマさんなら機能を追加すると思うのですが、相変わらず重箱の隅を突いています。


android.util.Xmlからorg.xmlpull.v1.XmlPullParserに書き換えてみたところ結果は非常に良好でした。


JavaSE6のStAXにはないnextText()の存在が大きいように感じるのですが、これはStAXのgetElementText()にて代用になるのでしょうか?ちょっと調査しないとわかりません。
なんにせよSAXのコンテントハンドラに比べて、コンテキスト処理を自前で記述する必要が減ったため、プログラムの記述量が減りました。itemタグの中であるかどうかだけは自前で記述しなければなりません。これは最初の1回のRSSタイトルだけスキップすれば良いのでループ前に飛ばしてしまえば良いのかもしれません。プル型の良さは全文検索する必要もないしコンテキストに従った手続き記述がある程度できることなので。


しかしこの状態ですと、android.util.Xmlを使う理由がほぼない気がするのですが何か利点があるのでしょうか?


なお、android.utilにはXmlPullAttributesというクラスがあってこれはXmlPullParserを突っ込んで使うようです。しかしJavaDoc上ではXmlPullParserがリンクされておらず説明の記述が公式ドキュメントには全くなかったりします。これが使用を禁止しているのかどうか、単にドキュメントの整備が遅れているのかどうか興味深いところです。

修正前

SAXのDefaultHandlerを用いていますが、charactersがガンですね。
ここはテキストが全文入ってくるとは限らず必ず自前で結合してタグ閉め時に取得せねばなりません。またコンテキスト制御のためにフラグを数多く用意しています。

public class RSSContentHandler extends DefaultHandler {
  
  private boolean inItem = false;
  private boolean inTitle = false;
  private boolean inLink = false;
  private PhotoResource currentPhoto;
  private ArrayList<PhotoResource> photos = new ArrayList<PhotoResource>();
  private StringBuilder mBuilder;

  public void characters(char[] ac, int i, int j) throws SAXException {
    if (inItem && (inTitle || inLink)) {
      assert(mBuilder != null);
      mBuilder.append(ac, i, j);
    }
  }

  @Override
  public void endElement(String s, String localName, String s2) throws SAXException {
    if (localName.equals("item")) {
      inItem = false;
      photos.add(currentPhoto);
      return;
    }
    
    if (!inItem) return;
    
    if (localName.equals("title")) {
      currentPhoto.setTitle(mBuilder.toString());
      Log.d("TEST", "Title = " + mBuilder.toString());
      inTitle = false;
    } else if (localName.equals("link")) {
      String real = mBuilder.toString();
      Log.d("TEST", "LinkChars = " + real);
      real = real.substring("http://4u.straightline.jp/image/".length());
      real = "http://www.straightline.jp/html/found/static/upload/l/l_" + real + ".jpg";
      
      currentPhoto.setUrl(real);
      Log.d("TEST", "URL = " + real);
      inLink = false;
    }
  }

  @Override
  public void startElement(String url, String localName, String qName,
      Attributes attr) throws SAXException {
    Log.d("TEST", url + "," + localName + "," + qName);
    
    if ("item".equals(localName)) {
      inItem = true;
      currentPhoto = new PhotoResource();
      return;
    }
    
    if (!inItem) return;
    
    if ("title".equals(localName)) {
      inTitle = true;
      mBuilder = new StringBuilder();
      return;
    } else if ("link".equals(localName)) {
      inLink = true;
      mBuilder = new StringBuilder();
      return;
    }
  }

修正後

  public void parse() {
    boolean inItem = false;
    
    XmlPullParserFactory factory;
    XmlPullParser xpp = null;
    try {
      factory = XmlPullParserFactory.newInstance();

      factory.setNamespaceAware(true);
      xpp = factory.newPullParser();
      URL huc = new URL(
//      "http://feeds.feedburner.com/ffffound/everyone?format=xml");
      "http://4u.straightline.jp/rss");
      InputStream is = huc.openStream();
      xpp.setInput(is, null);
      
      // Loop over XML input stream and process events 
      for (int e = xpp.getEventType(); e != END_DOCUMENT; e = xpp.next()) {
        String tag;
        switch (e) {
        case START_TAG:
          tag = xpp.getName();
          if ("item".equals(tag)) {
            inItem = true;
            currentPhoto = new PhotoResource();
            break;
          }
          if (!inItem) break;
          if ("title".equals(tag)) {
            currentPhoto.setTitle(xpp.nextText());
            Log.d("TEST", "Title = " + currentPhoto.getTitle());
          } else if ("link".equals(tag)) {
            String real = xpp.nextText();
            Log.d("TEST", "BEFORE = " + real);
            real = real.substring("http://4u.straightline.jp/image/".length());
            real = "http://www.straightline.jp/html/found/static/upload/l/l_" + real + ".jpg";
            currentPhoto.setUrl(real);
            Log.d("TEST", "URL = " + real);
          }
          break;
        case END_TAG:
          tag = xpp.getName();
          if ("item".equals(tag)) {
            inItem = false;
            photos.add(currentPhoto);
          }
          break;
        }
      } 
    } catch (Exception e) {
      Log.d("TEST", e.getMessage(), e);
    }
  }