既然知道了FeedParser类只是一个中介类,真正的解析工作都在各个版本号对应的解析器完成,那么下面就从RSS 0.9.x / 2.0系协议解析器开始学习。
★RSS_0_91_Parser
/** *//**
* Private constructor suppresses generation of a (public) default
* constructor.
*/
private RSS_0_91_Parser() {
}
/** *//**
* Holder of the RSS_0_91_Parser instance.
*/
private static class RSS_0_91_ParserHolder {
private static RSS_0_91_Parser instance = new RSS_0_91_Parser();
}
/** *//**
* Get the RSS_0_91_Parser instance.
*/
public static RSS_0_91_Parser getInstance() {
return RSS_0_91_ParserHolder.instance;
}
可以看到解析器都是单例模式,这里比较特别的是使用静态内部类来保存这个单例。 个人认为这里有点多余,静态内部类的作用主要有:
A.在创建静态内部类的实例前,不需要额外创建外部类的实例
B.静态内部类可以也只能访问外部类的静态成员和静态方法
内部类的方法通常用在一些隐藏实现细节的地方。通常这些类只对另外的某个类有意义,对其他类来说完全没有作用。如果定义在包里成为独立类,反而显得不协调。所以采用内部类的方式定义在需要使用它的类的内部,隐藏细节。
其次静态内部类的内部变量和方法都必须是静态的,这样我们才可以通过Outerclass.InnerClass.staticMethod的形式来引用他们。
这个类的重点是parse方法,我们都知道了肯定是先从channel节点开始解析。
Element channel = root.getChild("channel");
ChannelIF chnl = cBuilder.createChannel(channel, channel
.getChildTextTrim("title"));
首先获取XML Document的root节点下的channel子节点。然后利用Jdom提供的访问节点文本值的方法获取channel的title。接下来就是调用ChannelBuilder的createChannel了。在这里就用到了Java的多态特性,不同的实现使用不同的构建方法。如果是使用hibernate方法,那么则是如下的过程:
public ChannelIF createChannel(Element channelElement, String title,
String location) {
ChannelIF obj = null;
if (location != null) {
Query query = session
.createQuery("from Channel as channel where channel.locationString = ? ");
query.setString(0, location);
obj = (ChannelIF) query.uniqueResult();
}
if (obj == null) {
obj = new Channel(channelElement, title, location);
session.save(obj);
} else {
logger
.info("Found already existing channel instance with location "
+ location);
}
return obj;
}
先从数据库加载,如果找不到就创建然后持久化它。
下面的代码则是对channel下属的子节点和item的获取。
// 1..n item elements
List items = channel.getChildren("item");
Iterator i = items.iterator();
while (i.hasNext()) {
Element item = (Element) i.next();
ParserUtils.matchCaseOfChildren(item, new String[] { "title",
"link", "description", "source", "enclosure" });
// get title element
Element elTitle = item.getChild("title");
String strTitle = "<No Title>";
if (elTitle != null) {
strTitle = elTitle.getTextTrim();
}
if (logger.isDebugEnabled()) {
logger.debug("Item element found (" + strTitle + ").");
}
// get link element
Element elLink = item.getChild("link");
String strLink = "";
if (elLink != null) {
strLink = elLink.getTextTrim();
}
// get description element
Element elDesc = item.getChild("description");
String strDesc = "";
if (elDesc != null) {
strDesc = elDesc.getTextTrim();
}
// generate new RSS item (link to article)
ItemIF rssItem = cBuilder.createItem(item, chnl, strTitle, strDesc,
ParserUtils.getURL(strLink));
rssItem.setFound(dateParsed);
// get source element (an RSS 0.92 element)
Element source = item.getChild("source");
if (source != null) {
String sourceName = source.getTextTrim();
Attribute sourceAttribute = source.getAttribute("url");
if (sourceAttribute != null) {
String location = sourceAttribute.getValue().trim();
ItemSourceIF itemSource = cBuilder.createItemSource(
rssItem, sourceName, location, null);
rssItem.setSource(itemSource);
}
}
// get enclosure element (an RSS 0.92 element)
Element enclosure = item.getChild("enclosure");
if (enclosure != null) {
URL location = null;
String type = null;
int length = -1;
Attribute urlAttribute = enclosure.getAttribute("url");
if (urlAttribute != null) {
location = ParserUtils.getURL(urlAttribute.getValue()
.trim());
}
Attribute typeAttribute = enclosure.getAttribute("type");
if (typeAttribute != null) {
type = typeAttribute.getValue().trim();
}
Attribute lengthAttribute = enclosure.getAttribute("length");
if (lengthAttribute != null) {
try {
length = Integer.parseInt(lengthAttribute.getValue()
.trim());
} catch (NumberFormatException e) {
logger.warn(e);
}
}
ItemEnclosureIF itemEnclosure = cBuilder.createItemEnclosure(
rssItem, location, type, length);
rssItem.setEnclosure(itemEnclosure);
}
}
可以看到,对于这个解析过程,一般的步骤就是:
A.获取channnel下的某个子节点元素
B.如果该子节点元素有子元素或属性,则继续递归访问
C.调用该channnel子元素的createXxx方法加载或创建该子元素
D.调用Channel的setXxx方法添加该子元素到channel实例中
整个RSS 0.9.1协议的解析过程如下:
==================根元素==================
1. channel
==================必需元素==================
2. title
3.description
4.link
==================可选元素==================
5.language
6.item
7.image
8.textinput
9.copyright
10.rating
11.pubDate
12.lastBuildDate
13.docs
14.managingEditor
15.webMaster
16.cloud
★RSS_2_0_Parser
比较0.9.1和2.0协议,发现整个解析过程几乎相同。最大的不同有以下两点:
A.从RSS 2.0协议开始,增加了对名称空间(Namespace)的支持
B.增加了对几个2.0协议新增元素的解析
在RSS_2_0_Parser类中,每个元素的访问都需要使用name和namespace来区分,默认的namespace是""。其次在RSS 2.0的解析器中增加了对subject、category、author、creator、comments、guid这些元素的解析,这些在0.9.1协议中是没有的元素
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要尽力打好一手烂牌。
posted on 2009-12-30 10:45
Paul Lin 阅读(241)
评论(0) 编辑 收藏 所属分类:
J2SE