Android 开发之 XML 解析

来源:互联网 发布:默纳克调试软件app 编辑:程序博客网 时间:2024/05/16 11:31

转载请标明出处:【Gypsophila 的博客】 http://blog.csdn.net/astro_gypsophila/article/details/60509425

三种解析 XML 方法

  1. DOM 解析 XML
  2. SAX 解析 XML
  3. Pull 解析XML

示例的 XML 文件

<?xml version="1.0" encoding="UTF-8"?><students show="1">    <student id="1">        <name>            <firstName>张</firstName>            <lastName>三</lastName>        </name>        <age>20</age>    </student>    <student id="2">        <name>            <firstName>李</firstName>            <lastName>四</lastName>        </name>        <age>22</age>    </student>    <student id="3">        <name>            <firstName>王</firstName>            <lastName>五</lastName>        </name>        <age>22</age>    </student></students>

涉及的 Java 类:

public class StudentBean {    private int id;    private String firstName;    private String lastName;    private int age;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getFirstName() {        return firstName;    }    public void setFirstName(String firstName) {        this.firstName = firstName;    }    public String getLastName() {        return lastName;    }    public void setLastName(String lastName) {        this.lastName = lastName;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "StudentBean{" +                "id=" + id +                ", firstName='" + firstName + '\'' +                ", lastName='" + lastName + '\'' +                ", age=" + age +                '}';    }}public class StudentPlusBean {    //是否展示列表    private int show;    private List<StudentBean> students = new ArrayList<>();    public int getShow() {        return show;    }    public void setShow(int show) {        this.show = show;    }    public List<StudentBean> getStudents() {        return students;    }    @Override    public String toString() {        return "StudentPlusBean{" +                "show=" + show +                ", students=" + students +                '}';    }}

DOM 解析 XML

DOM 解析 XML 文件会将整个文件以文档树的形式存放到内存中,然后可以使用 DOM API 遍历 XML 树。因为加载 XML 文件所有内容到内存,所以内存消耗比较大,对于移动设备来说,建议采用 SAX 解析或者是 XML 文件内容比较小再采用 DOM。

public class DOMParseHelper {    public static void parse(Context context) {        StudentPlusBean studentPlusBean = new StudentPlusBean();        try {            // 获取工厂实例            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();            // 获取 DOM 解析器            DocumentBuilder builder = factory.newDocumentBuilder();            InputStream is = context.getAssets().open("config_students.xml");            // 调用 DOM 解析器解析整份文档,获取文档对象,document            Document document = builder.parse(is);            // 根节点 students            Node root = document.getFirstChild();            Element rootElement = (Element) root;            studentPlusBean.setShow(Integer.valueOf(rootElement.getAttribute("show")));            NodeList studentNodeList = document.getElementsByTagName("student");            for (int i = 0; i < studentNodeList.getLength(); i++) {                // 根据 tagname 获取的全是元素节点                StudentBean student = new StudentBean();                Element studentElement = (Element) studentNodeList.item(i);                // 解析属性 id                student.setId(Integer.valueOf(studentElement.getAttribute("id")));                // 方式一 (更加直观)                // 解析 name                // 因为很确定通过 tagName 方式查找一个 student 元素下,只会找到一个对应节点,且该节点为元素节点                // 所以直接 item(0) 且转型为 Element,剩下的查找也是同理                Element nameElement = (Element) studentElement.getElementsByTagName("name").item(0);                Element firstName = (Element) nameElement.getElementsByTagName("firstName").item(0);                student.setFirstName(firstName.getFirstChild().getNodeValue());                Element lastName = (Element) nameElement.getElementsByTagName("lastName").item(0);                student.setLastName(lastName.getFirstChild().getNodeValue());                // 解析 age                Element ageElement = (Element) studentElement.getElementsByTagName("age").item(0);                student.setAge(Integer.valueOf(ageElement.getFirstChild().getNodeValue()));                // 方式二/*                for (int j = 0; j < studentElement.getChildNodes().getLength(); j++) {                    Node studentContentNode = studentElement.getChildNodes().item(j);                    if (studentContentNode.getNodeType() == Node.ELEMENT_NODE) {                        Element studentContentElement = (Element) studentContentNode;                        if (studentContentElement.getNodeName().equals("name")) {                            // 解析 name 部分                            NodeList nameNodeList = studentContentElement.getChildNodes();                            for (int k = 0; k < nameNodeList.getLength(); k++) {                                Node nameDetailNode = nameNodeList.item(k);                                if (nameDetailNode.getNodeType() == Node.ELEMENT_NODE) {                                    Element nameDetailElement = (Element) nameDetailNode;                                    if (nameDetailElement.getNodeName().equals("firstName")) {                                        student.setFirstName(nameDetailElement.getFirstChild().getNodeValue());                                    } else if (nameDetailElement.getNodeName().equals("lastName")) {                                        student.setLastName(nameDetailElement.getFirstChild().getNodeValue());                                    }                                }                            }                        } else if (studentContentElement.getNodeName().equals("age")) {                            // 解析 age                            student.setAge(Integer.valueOf(studentContentElement.getFirstChild().getNodeValue()));                        }                    }                }*/                System.out.println("student = " + student);                studentPlusBean.getStudents().add(student);            }        } catch (ParserConfigurationException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (SAXException e) {            e.printStackTrace();        }    }}

说明:这边解析的写法方式都是因人而异,这边所谓的方式一和二不过想突出 Node 和 Element 的差别。方式二是按照文档树的层次结构,所以有很多层的循环,都是为了遍历每层元素节点,而所遍历到的节点并不都是元素节点。

DOM 解析方式至少需注意元素和节点的区别

SAX 解析 XML

Simple API for XML(简称SAX)是个循序存取XML的解析器API。

SAX 就像读取流一样做着解析工作,运行起来为单向。即无法再访问读取过的资料,除非再从头开始读取一遍。采用事件驱动,不需要解析整份文档,而是在读取文档过程中,判断读取到的字符是否符合 XML 某部分,符合就出发事件调用我们的所写回调函数。它是一个解析速度快且占用内存少的 XML 解析器,非常适合用于 Android 等移动设备。

用法:继承 DefaultHandler ,然后可选择性重写以下几个方法。

// 继承 DefaultHandler 并且重写五个方法public class SAXParseHelper extends DefaultHandler {    private StudentBean student;    private String tagName;    private StudentPlusBean studentPlusBean;    @Override    public void startDocument() throws SAXException {        super.startDocument();        // 判断读取到文档开始时触发,可以在此做初始化操作        studentPlusBean = new StudentPlusBean();    }    @Override    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {        super.startElement(uri, localName, qName, attributes);        // 读取到标签开始,这里可以获取所有属性        if (localName.equals("students")) {            studentPlusBean.setShow(Integer.valueOf(attributes.getValue("show")));        } else if (localName.equals("student")) {            student = new StudentBean();            student.setId(Integer.valueOf(attributes.getValue("id")));        }        tagName = localName;    }    @Override    public void characters(char[] ch, int start, int length) throws SAXException {        super.characters(ch, start, length);        // 读取到的内容部分        if (tagName != null) {            String data = new String(ch, start, length);            if (tagName.equals("firstName")) {                student.setFirstName(data);            } else if (tagName.equals("lastName")) {                student.setLastName(data);            } else if (tagName.equals("age")) {                student.setAge(Integer.valueOf(data));            }        }    }    @Override    public void endElement(String uri, String localName, String qName) throws SAXException {        super.endElement(uri, localName, qName);        // 对应标签结束        if (localName.equals("student")) {            studentPlusBean.getStudents().add(student);            System.out.println("student = " + student);            studentPlusBean.getStudents().add(student);            student = null;        }        tagName = null;    }    @Override    public void endDocument() throws SAXException {        super.endDocument();        //文档结束部分    }}// 外部调用方式private void parseXMLBySAX() {        try {            //获取文件资源建立输入流对象            InputStream is = getAssets().open("config_students.xml");            //创建解析处理器            SAXParseHelper helper = new SAXParseHelper();            //得到SAX解析工厂            SAXParserFactory factory = SAXParserFactory.newInstance();            //创建SAX解析器            SAXParser parser = factory.newSAXParser();            parser.parse(is, helper);        } catch (IOException e) {            e.printStackTrace();        } catch (ParserConfigurationException e) {            e.printStackTrace();        } catch (SAXException e) {            e.printStackTrace();        }    }

说明:SAX 较为高效!由于在解析过程中没有层级深入的感觉,所有的相关数据获取都是写在同一级的判断中,所以随着 XML 结构复杂,写法应该也越难理清,一堆的 if else 判断。(但我喜欢这种 SAX 和Pull,感觉好简便!:P)

PULL 解析 XML

Pull 解析器是 Android 内置用来解析 XML 文件。它的使用与 SAX 相似,采用事件驱动来完成 XML 解析。

    private void parseXMLByPULL() {        StudentPlusBean studentPlusBean = null;        StudentBean student = null;        try {            //1.直接获得实例            //XmlPullParser parser = Xml.newPullParser();            //2.使用工厂获得实例            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();            XmlPullParser parser = factory.newPullParser();            parser.setInput(getAssets().open("config_students.xml"), "utf-8");            int eventType = parser.getEventType();            while (eventType != XmlPullParser.END_DOCUMENT) {                switch (eventType) {                    case XmlPullParser.START_DOCUMENT:                        studentPlusBean = new StudentPlusBean();                        break;                    case XmlPullParser.START_TAG:                        if ("students".equals(parser.getName())) {                            studentPlusBean.setShow(Integer.valueOf(parser.getAttributeValue(0)));                        } else if ("student".equals(parser.getName())) {                            student = new StudentBean();                            // 取属性值方式1.明确所取属性的 index 时                            // student.setId(Integer.valueOf(parser.getAttributeValue(0)));                            // 取属性值方式2.直接根据属性的名称取出值                            student.setId(Integer.valueOf(parser.getAttributeValue(null,"id")));                        } else if ("firstName".equals(parser.getName())) {                            student.setFirstName(parser.nextText());                        } else if ("lastName".equals(parser.getName())) {                            student.setLastName(parser.nextText());                        }else if ("age".equals(parser.getName())) {                            student.setAge(Integer.valueOf(parser.nextText()));                        }                        break;                    case XmlPullParser.END_TAG:                        if ("student".equals(parser.getName())) {                            System.out.println("student = " + student);                            studentPlusBean.getStudents().add(student);                            student = null;                        }                        break;                }                // 下一个解析事件                eventType = parser.next();            }    } catch (XmlPullParserException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    }}

说明:是否感觉 SAX 与 Pull 方式很接近?都是对读取的标签事件时,做具体的逻辑处理,一般就是取出它们的属性或者值。

注意:Pull 解析中遇到若元素中含有元素和节点值都需要解析,应该 attribute 先解析,然后获取节点值。

比如 name 中的 firstName 还多了个 show 属性

...<firstName show="1"></firstName><lastName></lastName>...

应当注意获取解析属性和节点值的顺序,应当是先获取属性值,然后获取节点值。否则抛出 java.lang.IndexOutOfBoundsException

// 正确顺序else if ("firstName".equals(parser.getName())) {    System.out.println("show = "+parser.getAttributeValue(0));    System.out.println("firstName = "+parser.nextText());}// 错误顺序System.out.println("firstName = "+parser.nextText());System.out.println("show = "+parser.getAttributeValue(0));

点击 parser.nextText() 方法中查看注释,就可明白当解析到 eventType == TEXT

if(eventType == TEXT) {    String result = getText();    eventType = next();    if(eventType != END_TAG) {      throw new XmlPullParserException(         "event TEXT it must be immediately followed by END_TAG", this, null);     }     return result;}

执行了 next(),解析会走到 END_TAG 事件,即此时走到了</firstName>,这边肯定是没有 show 属性的,所以parser.getAttributeValue(0) 取不到第一个属性,发生越界。

0 0
原创粉丝点击