Android中XML的三种解析器分析、实战

XML解析器介绍


Android中提供了三种方式来解析XML:

  1. SAX(simple API for XML)
  2. DOM(文档对象模型)
  3. 以及Android内部使用的Pull解析。

SAX(simple API for XML)解析器

SAX(simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。而且相比于DOM,SAX可以在解析文档的任意时刻停止解析,但任何事物都有其相反的一面,对于SAX来说就是操作复杂。

SAX是事件驱动型的,SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

SAX解析器的优点
  • 解析速度快
  • 占用内存少

SAX解析器非常适合在Android移动设备中使用。

SAX解析器的缺点
  • 只能读取XML,无法修改。
  • 无法知道当前解析标签的上层标签及嵌套结构,仅仅知道当前解析的标签的名字和属性,要知道其他信息需要自己实现。
  • 无法随机访问某个标签。

DOM解析器

DOM(Document Object Model,文档对象模型)是W3C制定的一套规范标准,即规定了解析文件的接口。各种语言可以按照DOM规范去实现这些接口,给出解析文件的解析器。

DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。

DOM解析器的优点
  • 易用性强,使用简单
  • DOM在内存中以树形结构存放,因此检索和更新效率会更高
  • 适于修改XML结构
DOM解析器的缺点
  • 效率低
  • 解析速度慢
  • 内存占用量过高
  • 不适合大型文件

PULL解析器

PULL解析器是一个开源的java项目,既可以用在Android上,也可以用在java EE上,运行方式与SAX相似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。

PULL解析器的优点
  • 小巧轻便
  • 解析速度快
  • 简单易用
  • 非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器
PULL解析器的缺点

同SAX。

实战


我们将分别使用三种XML解析器来解析一个XML文件。

将要解析的XML文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<books>
  <book id="1">
    <name>android</name>
  </book>
  <book id="2">
    <name>java</name>
  </book>
  <book id="3">
    <name>c++</name>
  </book>
</books

SAX解析

public class SAXParserDemo {
    public List<Book> getBooks(InputStream instream) {
        try {
            //创建SAX解析工厂
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //创建SAX解析器
            SAXParser paser = factory.newSAXParser();
            //设置解析器的相关特性,true表示开启命名空间特性
            paser.setProperty("http://xml.org/sax/features/namespaces",true);
            //创建事件处理程序
            BookPaser bookPaser = new BookPaser();
            //开始解析
            paser.parse(instream, bookPaser);
            //关闭输入流
            instream.close();
            //返回解析后的内容
            return bookPaser.getBooks();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    //创建事件处理程序,也就是编写ContentHandler的实现类,一般继承自DefaultHandler类
    public final class BookPaser extends DefaultHandler {

        public List<Book> getBooks() {
            return books;
        }
        private List<Book> books = null;
        //当前解析的元素标签
        private String tagName = null; 
        private Book Book = null;
        //遇到文档开始标记的时候创建Book集合
        @Override
        public void startDocument () throws SAXException{
            books = new ArrayList<Book>();
        }
        
        //遇到元素节点开始时候的处理方法
        @Override
        public void startElement(String uri, String localName, String qName,
                                 Attributes attributes) throws SAXException {
            tagName = localName;
            //如果遇到<Book>标记,则创建一个Book实例
            if ("Book".equals(tagName)) {
                Book = new Book();
                //取出标记内的属性
                Book.setId(new Integer(attributes.getValue(0)));
            }
        }
        
        //遇到文本节点时的操作
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            //文本节点必须前面要有元素节点开始标记
            if (tagName != null) {
                //取出文本节点的值
                String data = new String(ch, start, length);
                //如果前面的元素节点开始标记是name
                if ("name".equals(tagName)) {
                    //则将文本节点的值赋值给Book的name
                    Book.setName(data);
                }
            }
        }

        //遇到元素节点结束时候的操作
        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            //如果遇到</Book>标记
            if ("Book".equals(localName)) {
                //则将创建完成的Book加入到集合中去
                books.add(Book);
                Book = null;//置空下一个Book
            }
            //置空已有标记,因为要解析下一个节点了
            tagName = null;
        }
    }
}

代码逻辑都包含在注释里了,SAX解析器是通过注册回调的方式实现解析逻辑的,我们的解析逻辑在DefaultHandler类型的子类中实现。

DOM解析

public class DomParserDemo {
    public List<Book> getBooks(InputStream instream) throws Exception {
        List<Book> books = new ArrayList<Book>();
        //创建DOM解析工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //创建DON解析器
        DocumentBuilder dombuild = factory.newDocumentBuilder();
        //开始解析XML文档并且得到整个文档的对象模型
        Document dom = dombuild.parse(instream);
        //得到根节点<books>
        Element root = dom.getDocumentElement();
        //得到根节点下所有标签为<book>的子节点
        NodeList bookList = root.getElementsByTagName("book");
        //遍历book节点
        for (int i = 0; i < bookList.getLength(); i++) {
            //首先创建一个Book
            Book book = new Book();
            //得到本次Book元素节点
            Element bookElement = (Element) bookList.item(i);
            //得到Book节点中的ID
            book.setId(new Integer(bookElement.getAttribute("id")));
            //得到Book节点下的所有子节点
            NodeList bookChilds = bookElement.getChildNodes();
            //遍历Book节点下的所有子节点
            for (int j = 0; j < bookChilds.getLength(); j++) {
                //如果是元素节点的话
                if (bookChilds.item(j).getNodeType() == Node.ELEMENT_NODE) {
                    //得到该元素节点
                    Element childElement = (Element) bookChilds.item(j); 
                    //如果该元素节点是name节点
                    if ("name".equals(childElement.getNodeName())) {
                        //得到name节点下的第一个文本子节点的值
                        book.setName(childElement.getFirstChild().getNodeValue());
                    }
                }
            }
            books.add(book);
        }
        return books;
    }
}

DOM的解析逻辑见注释。

PULL解析

public class PullParserDemo {
    public List<Book> getBooks(InputStream instream) throws Exception {
        List<Book> books = null;
        //当前处理对象
        Book book = null;
        //得到Pull解析器
        XmlPullParser parser = Xml.newPullParser();
        //设置输入流的编码
        parser.setInput(instream, "UTF-8");
        //得到第一个事件类型
        int eventType = parser.getEventType();
        //如果事件类型不是文档结束的话则不断处理事件
        while (eventType != XmlPullParser.END_DOCUMENT) {
            switch (eventType) {
                //如果是文档开始事件
                case (XmlPullParser.START_DOCUMENT):
                    //初始化Book集合
                    books = new ArrayList<Book>();
                    break;
                //如果遇到标签开始
                case (XmlPullParser.START_TAG):
                    //获得解析器当前元素的名称
                    String tagName = parser.getName();
                    //如果当前标签名称是<book>
                    if ("book".equals(tagName)) {
                        //创建一个book
                        book = new Book();
                        //将元素的属性值赋值给id
                        book.setId(new Integer(parser.getAttributeValue(0)));
                    }
                    //如果book已经创建完成
                    if (book != null) {
                        //如果当前节点标记是name
                        if ("name".equals(tagName)) {
                            book.setName(new String(parser.nextText()));
                        }
                    }
                    break;
                //如果遇到标签结束
                case (XmlPullParser.END_TAG):
                    //如果是book标签结束
                    if ("book".equals(parser.getName())) {
                        //将book加入集合
                        books.add(book);
                        //置空
                        book = null;
                    }
                    break;
            }
            //进入下一个事件处理
            eventType = parser.next();
        }
        return books;
    }
}

PULL解析是主动获取事件的,并且下一次事件的处理也由我们来调用触发。

至此,三种解析器的实例Demo已经完成,其实都不算复杂,按照Demo使用即可。

总结


本文分析了Android中,可使用的三种XML解析器,并对它们的实现逻辑及优缺点进行了分析和对比。

我们在实战部分,分别用三种解析器实现了Demo中XML文件的解析,代码注释详细的介绍了整个过程。

在Android中,推荐使用PULL解析器来解析XML文件,并且Android系统中,使用的XML解析器也是PULL解析器。

Android平台上,可以使用多种方式来解析XML文件,包括SimpleAPI for XML (SAX)、Document Object Model (DOM)和Android附带的pull解析器。 1. SAX解析器SAX解析器是一种基于事件驱动的解析器,它逐行读取XML文件并触发相应的事件。使用SAX解析器可以在解析过程逐步处理XML文件,而不需要将整个文件加载到内存。这种解析方式适用于处理大型XML文件,因为它可以减少内存的使用。 2. DOM解析器: DOM解析器将整个XML文件加载到内存,并构建一个树形结构表示XML文档。通过DOM API,可以遍历树形结构并获取所需的数据。但是,如果XML文件很大,DOM方式会占用大量的内存,并且可能导致性能下降。 3. Pull解析器Android平台还提供了一个轻量级的pull解析器,它也是一种基于事件驱动的解析器。与SAX解析器类似,pull解析器逐行读取XML文件并触发相应的事件。相比于SAX解析器pull解析器提供了更简单的API,并且在处理性能上更加高效。 下面是一个使用SAX解析器解析XML文件的示例代码: ```java import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.File; public class XMLParser { public static void main(String[] args) { try { File xmlFile = new File("path/to/xml/file.xml"); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); DefaultHandler handler = new DefaultHandler() { boolean isName = false; boolean isAge = false; public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equalsIgnoreCase("name")) { isName = true; } if (qName.equalsIgnoreCase("age")) { isAge = true; } } public void characters(char ch[], int start, int length) throws SAXException { if (isName) { System.out.println("Name: " + new String(ch, start, length)); isName = false; } if (isAge) { System.out.println("Age: " + new String(ch, start, length)); isAge = false; } } }; saxParser.parse(xmlFile, handler); } catch (Exception e) { e.printStackTrace(); } } } ``` 这段代码使用SAX解析器解析XML文件,并在遇到"name"和"age"标签时打印对应的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卜大爷

觉得不错的可以给我加油哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值