子供の時、アドベントカレンダー(チョコレートが入っているほう)でずいぶん楽しんでましたので、大人になった自分もこの季節を楽しめるようにPerl Advent Calendar(http://www.perladvent.org)のRSSリーダーを作成してみました。
RSSのXMLの構成はこんな構成
<entry>
<title>タイトル</title>
<id>URL</id>
<summary>要約</summary>
<updated>更新時間</updated>
</entry>
なので、以下のようなPOJOを作成しました。
public class Entry implements Serializable { //後でIntentのBundleに入れるようにSerializableを実装
private String title;
private String link;
private String summary;
private String updated;
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
//...長いので、linkとsummaryのゲッターとセッターを略します
public void setUpdated(String updated) {
String dateOnly = updated.substring(0, 10);
this.updated = dateOnly;
}
public String getUpdated() {
return this.updated;
}
}
SAXパーサーを使うことにしました。ハンドラークラスを使ってstartElementメソッドで適当なタグ名を見つけたらフラグを立って、中身のテキストを全部引っ張ってきて、最後にendElementメソッドでフラグを消すという訳ですが、なぜかSAXの仕様でcharactersのメソッドが何回も呼ばれることもあるらしくて、たまにテキストが2・3回読み込まれてて、困りました・・・
長い分を読み込むためにcharactersを複数回呼ぶことが必要と思うので、あまりいい解決方法ではないかと思いますけど、文字を追加してくれるbuilder.appendを呼ぶ直前にbuilder.setLength(0)でリセットすることでとりあえず解決できました・・・
builder.setLength(0); builder.append(ch, start, length);
奇麗に表示できました。
ハンドラークラスはこんな感じになっています:
public class RSSHandler extends DefaultHandler {
private ArrayList entries;
private Entry currentEntry;
private StringBuilder builder;
boolean inTitle;
boolean inLink;
boolean inSummary;
boolean inUpdated;
boolean inEntry;
public ArrayList getEntries() {
return this.entries;
}
@Override
public void startDocument() throws SAXException {
super.startDocument();
entries = new ArrayList();
builder = new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
super.startElement(uri, localName, name, attributes);
if (localName.equalsIgnoreCase(ENTRY)) {
this.currentEntry = new Entry();
inEntry = true;
}
if (localName.equalsIgnoreCase(TITLE)) {
inTitle = true;
}
if (localName.equalsIgnoreCase(LINK)) {
inLink = true;
}
if (localName.equalsIgnoreCase(SUMMARY)) {
inSummary = true;
}
if (localName.equalsIgnoreCase(UPDATED)) {
inUpdated = true;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
if (inEntry) {
if (inTitle) {
builder.setLength(0);
builder.append(ch, start, length);
}
if (inLink) {
builder.setLength(0);
builder.append(ch, start, length);
}
if (inSummary) {
builder.append(ch, start, length);
}
if (inUpdated) {
builder.setLength(0);
builder.append(ch, start, length);
}
}
}
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
super.endElement(uri, localName, name);
if (this.currentEntry != null) {
if (localName.equalsIgnoreCase(TITLE)) {
currentEntry.setTitle(builder.toString());
inTitle = false;
} else if (localName.equalsIgnoreCase(LINK)) {
currentEntry.setLink(builder.toString());
inLink = false;
} else if (localName.equalsIgnoreCase(SUMMARY)) {
currentEntry.setSummary(builder.toString());
inSummary = false;
} else if (localName.equalsIgnoreCase(UPDATED)) {
currentEntry.setUpdated(builder.toString());
inUpdated = false;
}
if (localName.equalsIgnoreCase(ENTRY)) {
entries.add(currentEntry);
inEntry = false;
}
}
}
}
そしてAsyncTaskでSAXを呼ぶことにして、出来上がったArrayListを返すことにしました。
@Override protected ArrayListdoInBackground(Void... params) { try { URL url = new URL(xmlLocation); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); RSSHandler handler = new RSSHandler(); xr.setContentHandler(handler); //ハンドラークラスを設定 xr.parse(new InputSource(url.openStream())); entries = handler.getEntries(); } catch (MalformedURLException e) { Log.e("LoadFeedData", "MalformedUrlException: ", e); } catch (Exception e) { Log.e("LoadFeedData", "Parsing exception", e); } return entries; }
後は、ArrayAdapterに渡して、そこで項目ごとにListViewにデーターを入れるだけです。
ちなみに、詳細はViewはこんな感じに表示してみました。コードはHighlighted Syntaxで読んだほうが楽なので、結局ブラウザーで読みたくて「ブラウザーで表示」みたいなボタンも用意しておきました(汗)
以前にJSONデータを読み込んでListViewで表示するようなアプリは作ったことはあったんですが、XMLは初めてですので、勉強になりました。SAX Parserの仕様をもっと深く勉強する必要があると思いますけど、一応、このRSSリーダーでアドベントカレンダーを読んでおきます。


