[Android面试] Xml 解析辨析---SAX、DOM、Pull

来源:互联网 发布:gis中地理数据的特征 编辑:程序博客网 时间:2024/06/07 03:03

 

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

SAX(Simple API XML),DOM(Document Objrect Model),以及Android推荐的Pull解析方式。


解析概念

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


DOM:即对象文档模型,它是将整个XML文档载入内存(所以效率较低,不推荐使用),使用DOM API遍历XML树、检索所需的数据,每一个节点当做一个对象。

Pull:运行方式与 SAX 解析器相似。它提供了类似的事件,SAX解析器的工作方式是自动将事件推入事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。

pull是一个while循环,随时可以跳出,而sax是只要解析了,就必须解析完成。


示例代码

SAX:



该接口中常用的方法:

/*      * 接收字符数据的通知。      * 在DOM中,ch数组从begin位置开始,长度为length的元素,     * 相当于Text节点的节点值(nodeValue)      */    public void characters(char[] ch, int begin, int length) throws SAXException;    /*      * 接收元素结束的通知。      * 参数意义如下:      *    uri:元素的命名空间      *    localName:元素的本地名称     *    qName:元素的限定名     *       */      public void endElement (String uri, String localName, String qName)     throws SAXException;    /*      * 接收文档的开始的通知。      */     public void startDocument () throws SAXException;    /*      * 接收元素开始的通知。      * 参数意义如下:     *    uri:元素的命名空间     *    localName:元素的本地名称(不带前缀)     *    qName:元素的限定名(带前缀)      *    atts:元素的属性集合     */    public void startElement (String uri, String localName, String qName, Attributes atts)    throws SAXException;

DOM:

可以使用 Dom4j来解析

示例代码:

import java.io.File;  import java.io.IOException;    import javax.xml.parsers.DocumentBuilder;  import javax.xml.parsers.DocumentBuilderFactory;  import javax.xml.parsers.ParserConfigurationException;    import org.w3c.dom.Document;  import org.w3c.dom.Element;  import org.w3c.dom.NamedNodeMap;  import org.w3c.dom.Node;  import org.w3c.dom.NodeList;  import org.xml.sax.SAXException;    public class DoXmlWithDOM {        public static void main(String[] args) {          File file = new File("F:/demo.xml");          (new DoXmlWithDOM()).readXML(file);      }        /*      * 读取XML(文档对象-根元素节点-所有的Element类型节点-Text类型节点的内容) ;      * 获取文档对象:DocumentBuilderFactory → DocumentBuilder → Document      */      public void readXML(File file) {          // ❶Ⅰ获得DocumentBuilderFactory          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();          try {              // ❷Ⅱ获得DocumentBuilder              DocumentBuilder builder = factory.newDocumentBuilder();              // ❸Ⅲ--获得文档对象--              Document doc = builder.parse(file);              // ❹Ⅳ获得根元素              Element element = doc.getDocumentElement();              // ❺Ⅴ用方法遍历递归打印根元素下面所有的ElementNode(包括属性,TextNode非空的值),用空格分层次显示.              listAllChildNodes(element, 0);// 参数0表示设定根节点层次为0,它的前面不打印空格.          } catch (ParserConfigurationException e) {              e.printStackTrace();          } catch (SAXException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }      }        /*      * 递归遍历并打印所有的ElementNode(包括节点的属性和文本节点的有效内容),按一般的xml样式展示出来(空格来表示层次)      */      public void listAllChildNodes(Node node, int level) {          // 只处理ElementNode类型的节点,感觉这种类型的节点(还有有效的文本节点)才是真正有用的数据,其他注释节点,空白节点等都用不上.          if (node.getNodeType() == Node.ELEMENT_NODE) {              boolean hasTextChild = false;// 变量表示该节点的第一个子节点是否就是一个有有效内容的文本节点)              // Ⅰ❶【打印 - 空格】空格的长度 - level(n级ElementNode有n个长度的空格在前面)              String levelSpace = "";              for (int i = 0; i < level; i++) {                  levelSpace += "    ";              }              // Ⅱ❷【打印 - 开始标签】先打印ElementNode的开始标签(有属性的话也要打印)              System.out.print(levelSpace + "<" + node.getNodeName()                      + (node.hasAttributes() ? " " : ">"));// 有属性的话节点的开始标签后面的尖括号">"就留待属性打印完再打印              // Ⅲ❸【打印 - 属性】遍历打印节点的所有属性              if (node.hasAttributes()) {                  NamedNodeMap nnmap = node.getAttributes();                  for (int i = 0; i < nnmap.getLength(); i++) {                      System.out.print(nnmap.item(i).getNodeName()                              + "=\""// 字符串里含双引号要用到转义字符\                              + nnmap.item(i).getNodeValue() + "\""                              + (i == (nnmap.getLength() - 1) ? "" : " "));// 不是最后一个属性的话属性之间要留空隙                  }                  System.out.print(">");// 开始标签里的属性全部打印完加上尖括号">"              }              // Ⅳ❹【打印 - 子节点】该ElementNode包含子节点时候的处理              if (node.hasChildNodes()) {                  level++;// 有下一级子节点,层次加1,新的层次表示的是这个子节点的层次(递归调用时传给了它)                  // 获得所有的子节点列表                  NodeList nodelist = node.getChildNodes();                  // 循环遍历取到所有的子节点                  for (int i = 0; i < nodelist.getLength(); i++) {                      // Ⅳ❹❶【有效文本子节点】子节点为TextNode类型,并且包含的文本内容有效                      if (nodelist.item(i).getNodeType() == Node.TEXT_NODE                              && (!nodelist.item(i).getTextContent()                                      .matches("\\s+"))) {// 用正则选取内容包含非空格的有效字符的文本节点                          hasTextChild = true;// 该ElementNode的一级子节点是存在有效字符的文本节点                          System.out.print(nodelist.item(i).getTextContent());// 在开始标签后面添加文本内容                          // Ⅳ❹❷【ElementNode子节点】子节点是正常的ElementNode的处理                      } else if (nodelist.item(i).getNodeType() == Node.ELEMENT_NODE) {                          System.out.println();                          // 递归调用方法 - 以遍历该节点下面所有的子节点                          listAllChildNodes(nodelist.item(i), level);// level表示该节点处于第几个层次(相应空格)                      }                  }                  level--;// 遍历完所有的子节点,层次变量随子节点的层数,依次递减,回归到该节点本身的层次                  // level++ 和 level--对于该节点的子节点影响的是子节点的初值              }              // Ⅴ❺【打印 - 结束标签】打印元素的结束标签.如果它的第一个一级子节点是有效文本的话,文本和结束标签添加到开始标签后面,              // 层次什么的就作废用不上了,否则,才按层次打印结束标签.              System.out.print(((hasTextChild) ? "" : "\n" + levelSpace) + "</"                      + node.getNodeName() + ">");          }      }    }  


Pull:

示例代码:

import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;import org.xmlpull.v1.XmlPullParserFactory;/** * 服务类 * @author 小明 * */public class Service {    /**     * 读取XML解析     *      * @param in     *            xml文件流     * @param encode     *            文件编码     * @return     */    public static List<Employee> readXml(InputStream in, String encode) {        List<Employee> emps = null;        Employee emp = null;        try {            // 创建解析器工厂对象            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();            // 从工厂中获取解析器对象            XmlPullParser parser = factory.newPullParser();            // 设置解析器输入            parser.setInput(in, encode);            /* 解析 */            // 获取解析到的事件类型            int eventType = parser.getEventType();            // 未解析到文档结尾,则循环解析            while (eventType != XmlPullParser.END_DOCUMENT) {                switch (eventType) {                case XmlPullParser.START_DOCUMENT: // 文档开始事件                    // 创建List集合对象                    emps = new ArrayList<Employee>();                    break;                case XmlPullParser.START_TAG: // 标签开始事件                    String nodeName = parser.getName().trim(); // 节点名称                    if ("employee".equals(nodeName)) { // employee节点                        emp = new Employee(); // 创建员工对象                        // 该节点有id属性,则获取id属性                        int id = Integer.parseInt(parser.getAttributeValue(                                null, "id"));                        emp.setId(id);                    } else if ("name".equals(nodeName)) { // name节点                        String name = parser.nextText().trim();                        emp.setName(name);                    } else if ("age".equals(nodeName)) { // age节点                        int age = Integer.parseInt(parser.nextText().trim());                        emp.setAge(age);                    } else if ("address".equals(nodeName)) { // address节点                        String address = parser.nextText();                        emp.setAddress(address);                    }                    break;                case XmlPullParser.END_TAG:                    if ("employee".equals(parser.getName().trim())) {                        emps.add(emp);                        emp = null;                    }                    break;                }                eventType = parser.next(); // 切换到下一个解析事件            }        } catch (XmlPullParserException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return emps;    }}




原创粉丝点击