Friday, September 12, 2008

เขียน Rome 's plugin

ถ้าจะต้องเขียนโปรแกรม parse feed, เราสามารถเลือกวิธีการได้หลายวิธี
แบบที่ง่ายที่สุดก็คือ ใช้พวก xml parser library ซึ่งมีอยู่มากมาย
เรื่องกวนใจที่ตามมาก็คือ feed มันมี format หลักๆอยู่ 2 แบบ คือ rss และ atom
โปรแกรม parser เราก็เลยฉลาดขึ้นมาอีกนิดหนึ่ง (จะเขียนโดยใช้ if หรือแยก function หรือแนว OOP ก็ว่าไป)

ในส่วนตัวผม ผมเลือกใช้ Rome (มันเป็น Java !!!)
ซึ่งเป็น api ในการ parse feed
โดยมันจะซ่อนความแตกต่างของโครงสร้างระหว่าง RSS และ ATOM ไว้จากเรา

ปัญหาที่เจอก็คือ กรณีที่เราไปดึง feed จาก FeedBurner
มันจะมี extension element จำนวนหนึ่งที่ Rome ไม่รู้จัก (ทำให้ไม่มี method ให้ get ค่าเหล่านั้น)
ยกตัวอย่าง Feed ของ Feedburner มันจะมีหน้าตาแบบนี้
<feed xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
...
<entry>
<title>xxx</title>
<content>yyyy<content>
<author>bact'</author>
<link href="http://feeds.feedburner.com/~r/bact/~3/326895937/hauntedness-management.html"/>
<feedburner:origLink href="http://bact.blogspot.com/2008/07/hauntedness-management.html"/>
</entry>
...
</feed>

สิ่งที่เราต้องการก็คือ origLink ซึ่ง ROME ไม่มี api ให้ใช้
แต่โชคดีที่คนออกแบบ Rome เขามองการณ์ไกล เขาก็เลยเตรียมช่อง plugin ให้เราไว้แล้ว

วิธีการก็คือ เราต้องสร้าง (High Level) Parser ของเราขึ้นมาตัวหนึ่ง
และทำการ register มันเข้ากับ Rome
เจ้า Parser ตัวนี้ จะถูกเรียกใช้หลังจากที่ Rome สร้าง DOM structure ขึ้นมาแล้ว
หน้าที่ของ parser ก็คือ return Object ที่ wrap ค่าที่เราต้องการไว้
ตัวอย่าง code
public class Parser implements ModuleParser {

public String getNamespaceUri() {
return FeedburnerModule.FEEDBURNER_URI;
}

public Module parse(Element element) {
Element origLink = element.getChild("origLink", FeedburnerModule.FEEDBURNER_NS);
if (origLink != null) {
FeedburnerModuleImpl impl = new FeedburnerModuleImpl(FeedburnerModule.class, FeedburnerModule.FEEDBURNER_URI);
impl.setOrigLink(origLink.getTextTrim());
return impl;
}
return null;
}

}

ในฝั่งคนใช้งาน ถ้าเขียนเป็น groovy ก็เขียนง่ายๆแบบนี้
def entry0 = feed.entries[0]
def module = entry0.getModule('http://rssnamespace.org/feedburner/ext/1.0')
assertEquals 'http://bact.blogspot.com/2008/07/hauntedness-management.html', module.origLink

Related link from Roti