首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > XML SOAP >

在 Android 上施用 XML

2013-03-26 
在 Android 上使用 XMLXML 解析器Java 的 Simple API for XML (SAX) 和 Document Object Model (DOM) 在 A

在 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 提要
<?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>


使用 SAX

SAX 实现
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);        }     }}



SAX 处理程序
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();        }    }}


Android SDK 提供了一个名称为 android.util.Xml 的实用类. Android SAX 解析器
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();    }}


经过简化的 Android SAX 解析器
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;    }}

新的 SAX 解析代码并未使用 SAX 处理程序,而是使用了 SDK 中的 android.sax 包中的类。这些类允许您构建 XML 文档的结构,并根据需要添加事件监听程序。在以上代码中,您声明文档将有一个 rss 根元素,并且它有一个 channel 子元素。然后,您声明 channel 将有一个 ITEM 子元素,并且开始添加监听程序。对于每个监听程序,您都使用了一个实现了特定接口(EndElementListner 或 EndTextElementListener)的匿名内部类。注意,您不需要跟踪字符数据。不仅仅因为这样会更加简单,更重要的是更加高效。最后,在调用 Xml.parse 实用方法时,您将传递一个通过根元素生成的处理程序

使用 DOM
基于 DOM 的提要解析器实现
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;    }}


XML pull 解析器
Android 并未提供对 Java StAX API 的支持。但是,Android 确实附带了一个 pull 解析器,其工作方式类似于 StAX。它允许您的应用程序代码从解析器中获取事件,这与 SAX 解析器自动将事件推入处理程序相反
基于 Pull 解析器的实现
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;    }}

pull 解析器的运行方式与 SAX 解析器相似。它提供了类似的事件(开始元素和结束元素),但您需要使用 (parser.next() 提取它们。事件将作为数值代码被发送,因此您可以使用一个简单 case-switch。注意,解析并未像 SAX 解析那样监听元素的结束,而是在开始处完成了大部分处理。在代码中,当某个元素开始时,您可以调用 parser.nextText() 从 XML 文档中提取所有字符数据。还需注意,您设置了一个标记(布尔变量 done)来确定何时到达感兴趣内容的结束部分。这允许您提早停止读取 XML 文档,因为您知道代码将不会关心文档的其余部分。这有时非常实用,特别是当您只需要访问一小部分 XML 文档时。通过尽快停止解析,您可以极大地减少解析时间。这种优化对于连接速度较慢的移动设备尤为重要。pull 解析器可以提供一些性能优势以及易用性。它还可以用于编写 XML。


创建 XML
使用 pull 解析器编写 XML
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);    } }


XmlSerializer 类是上面用pull解析xml所使用的 XmlPullParser 包的一部分。它没有提取事件,而是将它们推出到数据流或编写程序中。在本例中,它仅仅将事件推送到了一个 java.io.StringWriter 实例中。它提供了一个直观的 API,通过各种方法开始和结束文档、处理元素以及添加文本或属性。这是 StringBuilder 的一种出色的替换方案,因为它可以更加轻松地确保您的 XML 具有良好结构。

热点排行