在 Android 上使用 XML
XML 解析器
Java 的 Simple API for XML (SAX) 和 Document Object Model (DOM) 在 Android 上都是可用的。这些 API 多年以来一直都是 Java 技术的一部分。较新的 Streaming API for XML (StAX) 在 Android 中并不可用。但是, Android 提供了一个功能相当的库。最后,Java XML Binding API 在 Android 中也不可用。这个 API 已确定可以在 Android 中实现。但是,它更倾向于是一个重量级的 API,需要使用许多不同类的实例来表示 XML 文档。因此,这对于受限的环境,比如说 Android 针对的手持设备,不太理想。
基本提要解析器类
public abstract class BaseFeedParser implements FeedParser { // names of the XML tags static final String PUB_DATE = "pubDate"; static final String DESCRIPTION = "description"; static final String LINK = "link"; static final String TITLE = "title"; static final String ITEM = "item"; final URL feedUrl; protected BaseFeedParser(String feedUrl){ try { this.feedUrl = new URL(feedUrl); } catch (MalformedURLException e) { throw new RuntimeException(e); } } protected InputStream getInputStream() { try { return feedUrl.openConnection().getInputStream(); } catch (IOException e) { throw new RuntimeException(e); } }}
<?xml version="1.0" encoding="UTF-8"?><!-- generator="FeedCreator 1.7.2" --><rss version="2.0"> <channel> <title>android_news</title> <description>android_news</description> <link>http://www.androidster.com/android_news.php</link> <lastBuildDate>Sun, 19 Apr 2009 19:43:45 +0100</lastBuildDate> <generator>FeedCreator 1.7.2</generator> <item> <title>Samsung S8000 to Run Android, Play DivX, Take Over the World</title> <link>http://www.androidster.com/android_news/samsung-s8000-to-run-android-play-divx-take-over-the-world</link> <description>More details have emerged on the first Samsung handset to run Android. A yet-to-be announced phone called the S8000 is being reported ...</description> <pubDate>Thu, 16 Apr 2009 07:18:51 +0100</pubDate> </item> <item> <title>Android Cupcake Update on the Horizon</title> <link>http://www.androidster.com/android_news/android-cupcake-update-on-the-horizon</link> <description>After months of discovery and hearsay, the Android build that we have all been waiting for is about to finally make it out ...</description> <pubDate>Tue, 14 Apr 2009 04:13:21 +0100</pubDate> </item> </channel></rss>
public class SaxFeedParser extends BaseFeedParser { protected SaxFeedParser(String feedUrl){ super(feedUrl); } public List<Message> parse() { SAXParserFactory factory = SAXParserFactory.newInstance(); try { SAXParser parser = factory.newSAXParser(); RssHandler handler = new RssHandler(); parser.parse(this.getInputStream(), handler); return handler.getMessages(); } catch (Exception e) { throw new RuntimeException(e); } }}
import static org.developerworks.android.BaseFeedParser.*;public class RssHandler extends DefaultHandler{ private List<Message> messages; private Message currentMessage; private StringBuilder builder; public List<Message> getMessages(){ return this.messages; } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); builder.append(ch, start, length); } @Override public void endElement(String uri, String localName, String name) throws SAXException { super.endElement(uri, localName, name); if (this.currentMessage != null){ if (localName.equalsIgnoreCase(TITLE)){ currentMessage.setTitle(builder.toString()); } else if (localName.equalsIgnoreCase(LINK)){ currentMessage.setLink(builder.toString()); } else if (localName.equalsIgnoreCase(DESCRIPTION)){ currentMessage.setDescription(builder.toString()); } else if (localName.equalsIgnoreCase(PUB_DATE)){ currentMessage.setDate(builder.toString()); } else if (localName.equalsIgnoreCase(ITEM)){ messages.add(currentMessage); } builder.setLength(0); } } @Override public void startDocument() throws SAXException { super.startDocument(); messages = new ArrayList<Message>(); 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(ITEM)){ this.currentMessage = new Message(); } }}
public class AndroidSaxFeedParser extends BaseFeedParser { public AndroidSaxFeedParser(String feedUrl) { super(feedUrl); } public List<Message> parse() { RssHandler handler = new RssHandler(); try { Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, handler); } catch (Exception e) { throw new RuntimeException(e); } return handler.getMessages(); }}
public class AndroidSaxFeedParser extends BaseFeedParser { public AndroidSaxFeedParser(String feedUrl) { super(feedUrl); } public List<Message> parse() { final Message currentMessage = new Message(); RootElement root = new RootElement("rss"); final List<Message> messages = new ArrayList<Message>(); Element channel = root.getChild("channel"); Element item = channel.getChild(ITEM); item.setEndElementListener(new EndElementListener(){ public void end() { messages.add(currentMessage.copy()); } }); item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { currentMessage.setTitle(body); } }); item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { currentMessage.setLink(body); } }); item.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { currentMessage.setDescription(body); } }); item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { currentMessage.setDate(body); } }); try { Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler()); } catch (Exception e) { throw new RuntimeException(e); } return messages; }}
public class DomFeedParser extends BaseFeedParser { protected DomFeedParser(String feedUrl) { super(feedUrl); } public List<Message> parse() { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); List<Message> messages = new ArrayList<Message>(); try { DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parse(this.getInputStream()); Element root = dom.getDocumentElement(); NodeList items = root.getElementsByTagName(ITEM); for (int i=0;i<items.getLength();i++){ Message message = new Message(); Node item = items.item(i); NodeList properties = item.getChildNodes(); for (int j=0;j<properties.getLength();j++){ Node property = properties.item(j); String name = property.getNodeName(); if (name.equalsIgnoreCase(TITLE)){ message.setTitle(property.getFirstChild().getNodeValue()); } else if (name.equalsIgnoreCase(LINK)){ message.setLink(property.getFirstChild().getNodeValue()); } else if (name.equalsIgnoreCase(DESCRIPTION)){ StringBuilder text = new StringBuilder(); NodeList chars = property.getChildNodes(); for (int k=0;k<chars.getLength();k++){ text.append(chars.item(k).getNodeValue()); } message.setDescription(text.toString()); } else if (name.equalsIgnoreCase(PUB_DATE)){ message.setDate(property.getFirstChild().getNodeValue()); } } messages.add(message); } } catch (Exception e) { throw new RuntimeException(e); } return messages; }}
public class XmlPullFeedParser extends BaseFeedParser { public XmlPullFeedParser(String feedUrl) { super(feedUrl); } public List<Message> parse() { List<Message> messages = null; XmlPullParser parser = Xml.newPullParser(); try { // auto-detect the encoding from the stream parser.setInput(this.getInputStream(), null); int eventType = parser.getEventType(); Message currentMessage = null; boolean done = false; while (eventType != XmlPullParser.END_DOCUMENT && !done){ String name = null; switch (eventType){ case XmlPullParser.START_DOCUMENT: messages = new ArrayList<Message>(); break; case XmlPullParser.START_TAG: name = parser.getName(); if (name.equalsIgnoreCase(ITEM)){ currentMessage = new Message(); } else if (currentMessage != null){ if (name.equalsIgnoreCase(LINK)){ currentMessage.setLink(parser.nextText()); } else if (name.equalsIgnoreCase(DESCRIPTION)){ currentMessage.setDescription(parser.nextText()); } else if (name.equalsIgnoreCase(PUB_DATE)){ currentMessage.setDate(parser.nextText()); } else if (name.equalsIgnoreCase(TITLE)){ currentMessage.setTitle(parser.nextText()); } } break; case XmlPullParser.END_TAG: name = parser.getName(); if (name.equalsIgnoreCase(ITEM) && currentMessage != null){ messages.add(currentMessage); } else if (name.equalsIgnoreCase(CHANNEL)){ done = true; } break; } eventType = parser.next(); } } catch (Exception e) { throw new RuntimeException(e); } return messages; }}
private String writeXml(List<Message> messages){ XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); try { serializer.setOutput(writer); serializer.startDocument("UTF-8", true); serializer.startTag("", "messages"); serializer.attribute("", "number", String.valueOf(messages.size())); for (Message msg: messages){ serializer.startTag("", "message"); serializer.attribute("", "date", msg.getDate()); serializer.startTag("", "title"); serializer.text(msg.getTitle()); serializer.endTag("", "title"); serializer.startTag("", "url"); serializer.text(msg.getLink().toExternalForm()); serializer.endTag("", "url"); serializer.startTag("", "body"); serializer.text(msg.getDescription()); serializer.endTag("", "body"); serializer.endTag("", "message"); } serializer.endTag("", "messages"); serializer.endDocument(); return writer.toString(); } catch (Exception e) { throw new RuntimeException(e); } }