因此,您想要构建一个 XML 解析器来解析像这样的 RSS 提要。
<rss version="0.92">
<channel>
<title>MyTitle</title>
<link>http://myurl.com</link>
<description>MyDescription</description>
<lastBuildDate>SomeDate</lastBuildDate>
<docs>http://someurl.com</docs>
<language>SomeLanguage</language>
<item>
<title>TitleOne</title>
<description><![CDATA[Some text.]]></description>
<link>http://linktoarticle.com</link>
</item>
<item>
<title>TitleTwo</title>
<description><![CDATA[Some other text.]]></description>
<link>http://linktoanotherarticle.com</link>
</item>
</channel>
</rss>
现在您有两个可以使用的 SAX 实现。要么你使用org.xml.sax
or the android.sax
执行。在发布一个短手示例后,我将解释两者的优缺点。
android.sax 实现
让我们从android.sax
执行。
您首先必须使用以下命令定义 XML 结构RootElement
and Element
对象。
无论如何,我会使用 POJO(普通旧 Java 对象)来保存您的数据。这就是所需的 POJO。
频道.java
public class Channel implements Serializable {
private Items items;
private String title;
private String link;
private String description;
private String lastBuildDate;
private String docs;
private String language;
public Channel() {
setItems(null);
setTitle(null);
// set every field to null in the constructor
}
public void setItems(Items items) {
this.items = items;
}
public Items getItems() {
return items;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
// rest of the class looks similar so just setters and getters
}
这个类实现了Serializable
接口,以便您可以将其放入Bundle
并用它做一些事情。
现在我们需要一个类来保存我们的项目。在这种情况下,我只是要延长ArrayList
class.
项目.java
public class Items extends ArrayList<Item> {
public Items() {
super();
}
}
这就是我们的物品容器。我们现在需要一个类来保存每个项目的数据。
项目.java
public class Item implements Serializable {
private String title;
private String description;
private String link;
public Item() {
setTitle(null);
setDescription(null);
setLink(null);
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
// same as above.
}
Example:
public class Example extends DefaultHandler {
private Channel channel;
private Items items;
private Item item;
public Example() {
items = new Items();
}
public Channel parse(InputStream is) {
RootElement root = new RootElement("rss");
Element chanElement = root.getChild("channel");
Element chanTitle = chanElement.getChild("title");
Element chanLink = chanElement.getChild("link");
Element chanDescription = chanElement.getChild("description");
Element chanLastBuildDate = chanElement.getChild("lastBuildDate");
Element chanDocs = chanElement.getChild("docs");
Element chanLanguage = chanElement.getChild("language");
Element chanItem = chanElement.getChild("item");
Element itemTitle = chanItem.getChild("title");
Element itemDescription = chanItem.getChild("description");
Element itemLink = chanItem.getChild("link");
chanElement.setStartElementListener(new StartElementListener() {
public void start(Attributes attributes) {
channel = new Channel();
}
});
// Listen for the end of a text element and set the text as our
// channel's title.
chanTitle.setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
channel.setTitle(body);
}
});
// Same thing happens for the other elements of channel ex.
// On every <item> tag occurrence we create a new Item object.
chanItem.setStartElementListener(new StartElementListener() {
public void start(Attributes attributes) {
item = new Item();
}
});
// On every </item> tag occurrence we add the current Item object
// to the Items container.
chanItem.setEndElementListener(new EndElementListener() {
public void end() {
items.add(item);
}
});
itemTitle.setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
item.setTitle(body);
}
});
// and so on
// here we actually parse the InputStream and return the resulting
// Channel object.
try {
Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler());
return channel;
} catch (SAXException e) {
// handle the exception
} catch (IOException e) {
// handle the exception
}
return null;
}
}
正如您所看到的,这是一个非常简单的示例。使用的主要优点android.sax
SAX 实现是您可以定义必须解析的 XML 的结构,然后只需将事件侦听器添加到适当的元素即可。缺点是代码变得相当重复且臃肿。
org.xml.sax 实现
The org.xml.sax
SAX 处理程序的实现有点不同。
在这里,您不指定或声明 XML 结构,而只是监听事件。最广泛使用的是以下事件:
- 文档开始
- 文件结束
- 元素开始
- 元素结束
- 元素开始和元素结束之间的字符
使用上面的 Channel 对象的示例处理程序实现如下所示。
Example
public class ExampleHandler extends DefaultHandler {
private Channel channel;
private Items items;
private Item item;
private boolean inItem = false;
private StringBuilder content;
public ExampleHandler() {
items = new Items();
content = new StringBuilder();
}
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
content = new StringBuilder();
if(localName.equalsIgnoreCase("channel")) {
channel = new Channel();
} else if(localName.equalsIgnoreCase("item")) {
inItem = true;
item = new Item();
}
}
public void endElement(String uri, String localName, String qName)
throws SAXException {
if(localName.equalsIgnoreCase("title")) {
if(inItem) {
item.setTitle(content.toString());
} else {
channel.setTitle(content.toString());
}
} else if(localName.equalsIgnoreCase("link")) {
if(inItem) {
item.setLink(content.toString());
} else {
channel.setLink(content.toString());
}
} else if(localName.equalsIgnoreCase("description")) {
if(inItem) {
item.setDescription(content.toString());
} else {
channel.setDescription(content.toString());
}
} else if(localName.equalsIgnoreCase("lastBuildDate")) {
channel.setLastBuildDate(content.toString());
} else if(localName.equalsIgnoreCase("docs")) {
channel.setDocs(content.toString());
} else if(localName.equalsIgnoreCase("language")) {
channel.setLanguage(content.toString());
} else if(localName.equalsIgnoreCase("item")) {
inItem = false;
items.add(item);
} else if(localName.equalsIgnoreCase("channel")) {
channel.setItems(items);
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
content.append(ch, start, length);
}
public void endDocument() throws SAXException {
// you can do something here for example send
// the Channel object somewhere or whatever.
}
}
现在说实话,我真的无法告诉你这个处理程序实现相对于android.sax
一。不过,我可以告诉您现在应该非常明显的缺点。看一下 else if 语句startElement
方法。由于我们有标签<title>
, link
and description
我们必须在当前的 XML 结构中进行跟踪。也就是说如果我们遇到一个<item>
我们设置的起始标签inItem
标记为true
确保我们将正确的数据映射到正确的对象并在endElement
我们将该标志设置为的方法false
如果我们遇到一个</item>
标签。表示我们已经完成了该项目标签。
在这个例子中,管理起来非常容易,但是必须解析具有不同级别的重复标签的更复杂的结构就变得很棘手。在那里,您必须使用枚举来设置当前状态,并使用大量 switch/case 语句来检查您所在的位置,或者更优雅的解决方案是某种使用标签堆栈的标签跟踪器。