txqz memo

ROMEを使ってAtom1.0のパースをしたい

なんかAtomPPをとあるサービスで実装しようとして、Atom1.0とJavaのオブジェクトをうまいこと変えてくれるようなのを探している。最初はDOMやSAXで行こうと思ったけれども、これはいかにも面倒だ。そもそも、DOMで getElementsByTagName("title"); とかすると、 /feed/title も /feed/entry/title も含まれてしまうのでよくない。 getElementsByTagName() で得られたNodeListの0番目が /feed/title で残りが /feed/entry/title だと見なすもの危険だ。かといってSAXだと、オブザーバとか用意したり大変だし、要素の出現順序に制限がないのに一部の要素はfeedからentryへ値が継承されるというのを実装するのが厄介だ。まぁ、アレだったらXPathを使えばいいか。いや、そもそもAtom1.0はちゃんとRFCになっているわけだし、そんな公的なもののパーサをいちいち作らなければならないはずがない。

ということで今回はRomeを試してみた。JDOM1.0が必要なのであらかじめ用意しておく。試したもなにも、チュートリアル通りやっただけだ。

で、こんなAtom文書を読ませてみた。

<?xml version='1.0' encoding='UTF-8' ?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:txqz="http://txqz.net/" xmlns:xh="http://www.w3.org/1999/xhtml">
    <title>てきとうなfeed</title>
    <author><name>ようざか</name></author>
    <id>tag:txqz.net,2007-04-24:nanika</id>
    <updated>2007-04-24T21:16:32+09:00</updated>
    <entry>
        <title>朝起きて夜寝た</title>
        <category term="life" label="生活" />
        <content type="xhtml">
            <xh:div>
                <xh:p>こんにちは!こんにちは!</xh:p>
            </xh:div>
        </content>
        <id>tag:oshira.se,2000:212</id>
        <link rel="alternate" href="http://txqz.example.com/blog/212" />
        <summary>朝のあいさつです><</summary>
        <updated>2000-01-02T03:04:05+06:00</updated>
        <txqz:exp>なんかentryの拡張要素</txqz:exp>
    </entry>
    <txqz:exp>なんかfeedの拡張要素1</txqz:exp>
    <txqz:exp>なんかfeedの拡張要素2</txqz:exp>
</feed></code></pre>
<p>Java的にはこんな感じ:</p>
<pre><code class="java">SyndFeedInput input = new SyndFeedInput();
SyndFeed feed = input.build(new File("test.xml"));</code></pre>
<p>とりあえず各entryについて、title,id,updated,contentの各要素の値と、rel属性が"alternate"であるlink要素のhref属性の値を保管するようなクラスに代入していきたい。それが終わったら独自拡張要素txqz:exp (entry要素内に0回か1回出現するものとする) もクラスに代入してみよう。</p>
<p>保管クラスは以下のような感じ:</p>
<pre><code class="java">class Entry{
    private String title;
    private String id;
    private String link;
    private String date;
    private String content;
    private String exp;
    // 以下setterとgetter
}

ROME側で:

List entries = feed.getEntries();
List<Entry> entryList = new ArrayList<Entry>();
for(Object o : entries){
    SyndEntry entry = (SyndEntry)o;
    Entry e = new Entry();
    e.setTitle(entry.getTitle());
    e.setId(entry.getUri());
    e.setLink(entry.getLink());
    e.setDate(entry.getUpdatedDate());
    SyndContent content = (SyndContent)entry.getContents().get(0);
    e.setContent(content.getValue());
    entryList.add(e);
}

それぞれgetして表示してみると:

朝起きて夜寝た
tag:txqz.example.com,2000:212
http://txqz.example.com/blog/212
Sun Jan 02 06:04:05 JST 2000
<xh:div xmlns:xh="http://www.w3.org/1999/xhtml"><xh:p>こんにちは!こんにちは!</xh:p></xh:div>

link[@rel='alternate']/@hrefgetLink() で取得できるけれども、rel属性がほかの値になっているlink要素のhref属性値はどうやって取得するのだろう。あと、 SyndEntry#getContents()List を返すメソッドで、単純に SyndContent を返すようなメソッドは用意されていないみたい。RFC4287 にはatom:entry elements MUST NOT contain more than one atom:content element.とあるので getContents() メソッドってどうよ? という感じだが、ROMEはRSSやAtom0.3など多くのフォーマットに対応しているのがウリなので、そこら辺は仕方ない。あと時刻の値が Date#toString() な感じになっているのが興味深い。MySQLに入れるには SimpleDateFormat かなんかを使ってパースしなおさないといけなくなるぞ。


Dateな感じも何も、最初からDateオブジェクトが入っているだけだった。MySQLが受け取ってくれるような文字列に変えたければ、フツーに

new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format();

とかやったらいい。


それはそれとして、独自拡張要素txqz:expの取得をしてみたい。 SyndEntry#getForeignMarkup() を使う。さっきのforループで entryList.add(e); する前に:

    ArrayList list = (ArrayList) entry.getForeignMarkup();
    Element exp = (Element) list.get(0);
    e.setExp(exp.getValue());

SyndEntry#getForeignMarkup() が返すObjectの正体はArrayList で、その中にはjdomの Element が入っている。なので、「何番目の独自拡張要素がほしい!」というのはできるが、「何々という名前の独自拡張要素がほしい!」と思ってもループをまわして Element#getName() して条件分岐する必要が出てきてしまう。うーん。

なんかもっとこう、Atom1.0だったらこのパーサに任せておけば間違いない! みたいなものはないものか。引き続き探索中。