【coder-pig教程学习笔记4】xml文件解析

1.xml 是什么
全名叫 可扩展标记语言,一般用来存储数据,可以看作一个微型的数据库,比如 SharePreference 就是适用 xml 文件保存配置信息,SQLite 的底层也是一个 xml 文件。把数据包装成一个 xml 文件进行传递也是常用的做法。


2.xml 文件的内容结构

<?xml version="1.0" encoding="UTF-8"?>(文档声明,声明当前文档为 xml 格式,采用 UTF-8 编码)
<persons>(开始元素(persons))
    <person id="1">(文本节点(空白文本) 开始元素(person) 属性)
        <name>nameA</name>(文本节点(空白文本) 开始元素(name) 文本节点 结束元素(name))
        <age>ageOne</age>(文本节点(空白文本) 开始元素(age) 文本节点 结束元素(age))
    </person>(文本节点(空白文本) 结束元素(person))
    <person id="2">
        <name>nameB</name>
        <age>ageTwo</age>
    </person>
</persons>(结束元素(persons))

!缩进位置的空白处也是文本节点,为空白文本。


3.解析 xml 文件的三种方法

1.SAX

  • 处理形式:对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束等地方时通知事件处理函数,由事件处理函数执行相应动作,然后继续同样的扫描,直到文档末尾。采用流式解析,解析是同步的,读到哪里就处理到哪里。
  • 优点:解析速度快,占用内存少。
  • 缺点:每需要解析一类xml,就需要编写新的适合该类xml的处理类,用起来比较麻烦。
  • 用法:继承 Android 提供的帮助类 DefaultHandler ,重写对应 的方法:
startDocument(); //当读取到文档开始标志时触发,通常在这里完成一些初始化操作

endDocument(); //文档结束部分,在这里完成一些善后工作

startElement(String uri, String localName, String qName, Attributes attributes); //参数依次为(命名空间,不带命名空间的前缀标签名,带命名空间前缀的标签名,通过atts可以得到所有的属性名和相应的值), SAX 中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会记录下以前所碰到的标签,也就是说,在 startElement() 方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子属性等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成,这使得 SAX 在编程处理上没有 DOM 来得方便。

endElement(String uri, String localName, String qName); //在遇到结束标签时,调用这个方法

charracters(char[] ch, int start, int length) //这个方法用来处理在 xml 文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用 new String(char[] ch, int start, int length) 就可以获取内容

代码栗子:

创建一个 xml 文件: person.xml ,放到 assets 目录下:

<?xml version="1.0" encoding="UTF-8"?>
<persons>
    <person id = "11">
        <name>SAX解析</name>
        <age>18</age>
    </person>
    <person if = "13">
        <name>XML1</name>
        <age>43</age>
    </person>
</persons>

SAX 解析类: SAXHelper.java:

public class SAXHelper extends DefaultHandler {
    private Person person;
    private ArrayList<Person> persons;
    //当前解析的元素标签
    private String tagName = null;

    //当读取到文档开始标志时触发,通常在这里完成一些初始化操作
    @Override
    public void startDocument() throws  SAXException {
        this.persons = new ArrayList<Person>();
        Log.d("SAX", "读取到文档头,开始解析 xml");
    }

    //读到一个开始标签时触发,第二个参数为标签名,最后一个参数为属性数组
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (localName.equals("person")){
            person = new Person();
            person.setId(Integer.parseInt(attributes.getValue("id")));
            Log.d("SAX", "开始处理person元素");
        }
        this.tagName = localName;
    }

    //读取内容,第一个参数为字符串内容,后面依次为起始位置与长度
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        //判断当前标签是否有效
        if (this.tagName != null) {
            String data = new String(ch, start, length);
            //读取标签中的内容
            if (this.tagName.equals("name")) {
                this.person.setName(data);
                Log.d("SAX", "处理 name 元素内容");
            }else if (this.tagNmae.equals("age")) {
                this.person.setAge(Integer.parseInt(data));
                Log.d("SAX", "处理 age 元素内容");
                }
            }
        }

    //处理元素结束时触发,这里讲对象添加到集合中
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (localName.equals("person")) {
            this.persons.add(person);
            person = null;
            Log.d("SAX", "处理 person 元素结束");
        }
        this.tagName = null;
    }

    //读取到文档结尾时触发
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        Log.i("SAX", "读取到文档尾, xml 解析结束");
    }

    //获取 person 集合
    public ArrayList<Person> getPersons() {
        return persons;
    }
}

在 MainActivity.java 中写上这样一个方法,要解析 xml 时就调用

private ArrayList<Person> readxmlForSAX() throws Exception {
    //获取文件资源建立输入流对象
    InputStraem is = getAssets().open("person.xml");
    //创建 SAX 解析类对象
    SAXHelper ss = new SAXHelper();
    //得到 SAX 解析工厂
    SAXParserFactory factory = SAXParseFactory.newInstance();
    //创建 SAX 解析器
    SAXParser parser = factory.newSAXParser();
    //将 xml 解析处理器分配给解析器,对文档进行解析,将事件发送给处理器
    parser.parse(is, ss);
    is.close();
    return ss.getPersons();
}

2.DOM

  • 处理形式:先把 xml 文档都读取到内存中,然后再用 DOM API 来访问树形结构,并获取数据。
  • 优点:写起来简单
  • 缺点:很消耗内存,假如读取的数据量较大,手机内存不够的话,可能导致手机死机,建议只用于解析内容少的 xml 文件
  • 用法
    API:
//解析器工厂类
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

//解析器类,通过解析器工厂类来获得
DocumentBUilder dbBuilder = dbFactory.newDocumentBuilder();

//文档树模型,将要解析的 xml 文件读入 DOM 解析器
//Document 对象代表了一个 xml 文档的模型树,所有的其他 Node 都 以一定的顺序包含在 Document 对象之内, 排列成一个树状结构,以后对 xml 文档的所有操作都与解析器无关
Document doc = dbBuilder.parse(context.getAssets().open("person.xml"));

//节点列表类 NodeList ,代表一个包含一个或多个 Node 的列表,可看作数组,有以下两个方法
item(int index); //返回集合的第 index 各 Node 项
getLength(); //列表的节点数

//节点类 Node ,DOM 中最基本的对象,代表文档树中的抽象节点,很少会直接使用的,通常是调用它的子对象 Element、Attr、Text 等

//元素类 Element ,Node 最主要的子对象,在元素中可以包含属性,因此有取属性的方法:getAttribute()获得属性值, getTagName() 获得元素的名称

//属性类 Attr , 代表某个元素的属性,Attr 实现 Node 接口,虽然 Attr 是包含在 Element 中的,但并不能将其看作是 Element 的子对象, 因为 Attr 并不是 DOM 树的一部分 

代码栗子:
DOM 解析类:DOMHelper.java

public class DOMHelper {
    public static ArrayList<Person> queryXML(Context context){
        ArrayList<Person> persons = new ArrayList<Person>();
        try {
            //获得 DOM 解析器工厂实例
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            //从 DOM 工厂获得 DOM 解析器
            DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
            //把要解析的 xml 文件读入 DOM 解析器
            Document doc = dbBuilder.parse(context.getAssets().open("person.xml"));
            Log.d("处理该文档的 DomImplementation 对象=" + doc.getImplementation());
            //得到文档总名称为 person 的元素的节点列表
            NodeList nList = doc.getElementsByTagName("person");
            //遍历该集合,显示集合中的元素以及子元素的名字
            for (int i = 0; i < nList.getLength(); i++) {
                //先从 Person 元素开始解析
                Element personElement = (Element) nList.item(i);
                Person person = new Person();
                person.setId(Integer.valueOf(personElement.getAttribute("id")));

                //获取 person 下的 name 和 age 的 Node 集合
                NodeList childNodeList = personElement.getChildNodes();
                for(int j = 0; j < childNodeList.getLength(); j++) {
                    NOde childNOde = childNodeList.item(j);
                    //判断子 Node 类型是否为元素 Node
                    if (childeNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element childElement = (Element) childNode;
                    if("name".equals(childElement.getNodeName()))
                        person.setName(childElement.getFirstChild().getNodeValue());
                    else if ("age".equals(childElement.getNodeName()))
                        person.setAge(Integer.valueOf(childElement.getFirstChild().getNodeValue();
                    }
                }
                persons.add(person);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return persons;
    }
}

3.PULL

  • 处理形式:PULL 的编码比较简单,只需处理开始和结束事件,通常是使用 switch 语句,根据事件的 不同类型,匹配不同的处理方式,有5种事件:START_DOCUMENT , START_TAG , TEXT , END_TAG , END_DOCUMENT 。 当某个元素开始时,可以调用 parser.nextText() 从 xml 文档中提取所有字符数据,当解析到一个文档结束时,自动生成 EndDocument 事件。在 PULL 解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像 SAX 那样由处理器触发一种事件的方法然后执行我们的代码,读取到 xml 的声明返回 START_DOCUMENT,结束返回 END_DOCUMENT,开始标签返回 START_TAG,结束标签返回 END_TAG, 文本返回 TEXT。
  • 优点:和 SAX 差不多,但代码写起来比 SAX 简单些,适合移动设备使用
  • 用法
    使用 PULL 解析 xml:
    第一步:获得一个 XmlPullParser 类的引用,有两种方法
//通过 xml 解析工厂获得实例
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();

//直接获得实例
XmlPullParser parser = Xml.newPullParser();

第二步:为 parser 解析器对象提供 xml 流与编码格式

parser.setInput(xml, "UTF-8");

第三步:获得时间的类型

int eventType = parser.getEvent();

第四步:用 switch 对不同的事件类型进行处理

while (eventType != XmlPullParser.END_DOCUMENT) {
    switch (eventType){ 
        //START_DOCUMENT,开始读文档是触发,在这里完成一些初始化操作
        case XmlPullParser.START_DOCUMENT:
            persons = new ArrayList<Person>();
            break;
        //START_TAG,开始读标签,通过 parser 的 getName()方法获得标签名进行比较
        //使用 getAttributeValue(index) 可取出属性的值
        case XmlPullParser.START_TAG:
            if("person".equals(parser.getName())) {
                person = new Person();
                //取出属性值
                int id = Integer.parseInt(parser.getAttributeValue(0)));
                person.setId(id);
            }else if ("name".equals(parser.getName())) {
                String name = parser.nextText(); //获取该节点的内容
                person.setName(name);
            }else if ("age".equals(parser.getName())) {
                int age = Integer.parseInt(parser.nextText());
                person.setAge(age);
            }
            break;
        case XmlPullParser.END_TAG:
            if("person".equals(parser.getName())) {
                persons.add(person);
                person = null;
            }
            break;
        }
        eventType = parser.next(); //循环解析下一个元素
}

完整代码:

public static ArrayList<Person> getPersons(InputStream xml) throws Exception {
    ArrayList<Person> persons = null;
    Person person = null;
    //创建一个 xml 解析的工厂
    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    //获得 xml 解析类的引用
    XmlPullParser parser = factory.newPullParser();
    //为 xml 解析器提供 xml 输入流和编码格式
    parser.setInput(xml, "UTF-8");
    //获得事件类型
    int eventType = parser.getEventType();
    while (eventType != XmlPullParser.END_DOCUMENT) {
        switch (eventType) {
            case XmlPullParser.START_DOCUMENT:
                persons = new ArrayList<Person>();
                break;
            case XmlPullParser.START_TAG:
                if("person".equals(parser.getName())) {
                    person = new Person();
                    //取出属性值
                    int id = Integer.parseInt(parser.getAttributeValue(0));
                    person.setId(id);
                }else if ("name".equals(parser.getName())) {
                    String name = parser.nextText();
                    person.setName(name);
                }else if ("age".equals(parser.getName())) {
                    int age = Integer.parseInt(parser.nextText());
                    person.setAge(age);
                }
                break;
            case XmlPullParser.END_TAG:
                if ("person".equals(parser.getName())) {
                    persons.add(person);
                    person = null;
                }
                break;
            }
            eventType = parser.next();
        }
        return persons;
    }

使用 PULL 生成 xml 文件:
第一步:创建 XmlSerializer(xml 序列化类)的实例

XmlSerializer serializer = Xml.newSerializer();

第二步:为 XmlSerializer 设置输出流与编码格式

serializer.setOutput(out, "UTF-8");

第三步:为 XmlSerializer 设置 xml 的编码格式

serializer.startDocument("UTF-8", true);

第四步:设置根元素

serializer.startTag(null, "persons");

第五步:使用增强 for 循环遍历 persons 集合中的所有元素,同时依次写入标签与属性

for (Person p : persons) {
    serializer.startTag(null, "person");
    serializer.attribute(null, "id", p.getId() + "");
    serializer.startTag(null, "name");
    serializer.text(p.getName());
    serializer.endTag(null, "name");
    serializer.startTag(null, "age");
    serializer.text(p.getAge() + "");
    serializer.endTag(null, "age");
    serializer.endTag(null, "person");
}

第六步:设置根完结元素

serializer.endTag(null, "persons");

第七步:结束文档编写

serializer.endDocument();

第八步:调用 flush() 将内存中的数据写入文件中并关闭输出流

out.flush();
out.close();

完整代码:

public static void save(List<Person> persons, OutputStream out) throws Exception {
    XmlSerializer serializer = Xml.newSerializer();
    serializer,.setOutput(out, "UTF-8");
    serializer.startDocument("UTF-8", true);
    serializer.startTag(null, "persons");
    for (person p: persons) {
        serializer.startTag(null, "person");
        serializer.attribute(null, "id", p.getId() + "");
        serializer.startTag(null, "name");
        serializer.text(p.getName());
        serializer.endTag(null, "name");
        serializer.startTag(null, "age");
        serializer.text(p.getAge() + "");
        serializer.endTag(null, "age");
        serializer.endTag(null, "person");
    }
serializer.endTag(null, "persons");
serializer.endDocument();
out.flush();
out.close();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值