Android-使用SAX对XML进行增删改查

作者: CokeNello | 来源:发表于2018-01-14 23:14 被阅读254次

    0. Thanks

    Android解析XML的三种方式
    Android几种解析XML方式的比较
    android xml 解析 修改
    android 对xml文件的pull解析,生成xml ,对xml文件的增删

    1.概述

    • SAX是一中事件驱动类型的XML解析方式。说白了,就是通过复写一个Default类去告知,解析的结果。SAX并不会想DOM那样把整个的XML加载到内存中,而它会像IO流那样,一个一个标签地去解析。

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

    • 为了方便说明,先约定好一个XML如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <persons>
        <person id="1" key="33" type="type">
            <name>zhangsan</name>
            <age>21</age>
        </person>
    </persons>
    

    2.基本读取(查)

    • 代码如下
    SAXParserFactory factory = SAXParserFactory.newInstance();//创建SAX解析工厂
    SAXParser saxParser;
    try {
        File file = new File(xmlFilePath);
        InputStream inputStream = new FileInputStream(file);//得到输入流
        saxParser = factory.newSAXParser();//创建解析器
        saxParser.parse(inputStream,new DefaultHandler(){//开始解析
            //文档开始标记
            @Override
            public void startDocument() throws SAXException {
                super.startDocument();
                Log.i("loadWithSax","startDocument");
            }
            //文档结束标记
            @Override
            public void endDocument() throws SAXException {
                super.endDocument();
                FileUtils.closeIO(inputStream);
                Log.i("loadWithSax","endDocument");
            }
            //解析到标签
            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SA
                super.startElement(uri, localName, qName, attributes);
                Log.i("loadWithSax","startElement"+",uri:"+uri+",localName:"+localName+",qName:"+qName);
                if (attributes!=null) {
                    for (int i = 0; i < attributes.getLength(); i++) {
                        Log.i("loadWithSax",attributes.getLocalName(i)+","+attributes.getValue(i)+","+attributes.
                    }
                }
            }
            //标签解析结束
            @Override
            public void endElement(String uri, String localName, String qName) throws SAXException {
                super.endElement(uri, localName, qName);
                Log.i("loadWithSax","endElement"+",uri:"+uri+",localName:"+localName+",qName:"+qName);
            }
            /**
             *  文本
             *  该方法中的ch把所解析的xml的所有数据都保存进来,且ch初始化为2K数据。 start是一个节点">"的位置。length就是">"到下一个"<"的长度。
             *  <namesList>
             *      <name>michael</name>
             *  </namesList>
             *  执行namesList节点时,因为没有文本,
             *  不会执行到该方法。
             */
            @Override
            public void characters(char[] ch, int start, int length) throws SAXException {
                super.characters(ch, start, length);
                Log.i("loadWithSax","characters"+",start:"+start+",length:"+length);
                for (int i = 0; i < ch.length; i++) {
                    Log.i("loadWithSax","char:"+ch[i]+",ASCII:"+(int)ch[i]);
                }
            }
            //警告回调
            @Override
            public void warning(SAXParseException e) throws SAXException {
                super.warning(e);
                Log.i("loadWithSax","warning"+","+e.getMessage());
            }
            //错误回调
            @Override
            public void error(SAXParseException e) throws SAXException {
                super.error(e);
                Log.i("loadWithSax","error1"+","+e.getMessage());
            }
        });
    } catch (ParserConfigurationException | SAXException | IOException e) {
        e.printStackTrace();
        Log.i("loadWithSax","error2"+","+e.getMessage());
    }
    
    • 传入:DefaultHandler的实体,通过复写其中的方法,查询到文档,标签的内容:
    • startDocument 和 endDocument是扫描文档的开始和结束

    • startElement,是解析到了标签,localName就是标签的名称,如本文所示例的,当解析到第一个人名的时候,

    <person id="1" key="33" type="type">
        <name>zhangsan</name>
        <age>21</age>
    </person>
    

    解析到<person></person>回调:startElement,标签内的参数是Attributes attributes,一个for循环就可以遍历读取。

    • characters,解析到标签的内容时候回调,接着上面例子,解析<person></person>,回调startElement,然后不会回调此方法,因为内容不是文本,而是包含了标签,所以,解析到其子标签:<name>zhangsan</name>的时候,又会先回调回调startElement,然后,才回调characters,告诉你,这个标签里面有文本内容!参数说明如下:

    • char[] : 内容字符数组里面。如:<name>zhangsan</name>,char[]就是:{'z','h','a','n','g','s','a','n'}

    • start :0,文本的开始

    • length :文本的长度。

    • endElement,标签结束。

    • 使用上面的代码,得到的部分log如下:

    I/loadWithSax: startDocument
    I/loadWithSax: startElement,uri:,localName:persons,qName:persons
    I/loadWithSax: characters,start:0,length:1
    I/loadWithSax: char:
                   ,ASCII:10
    I/loadWithSax: characters,start:0,length:1
    I/loadWithSax: char:    ,ASCII:9
    I/loadWithSax: startElement,uri:,localName:person,qName:person
    I/loadWithSax: id,1,CDATA
    I/loadWithSax: key,33,CDATA
    I/loadWithSax: type,type,CDATA
    I/loadWithSax: characters,start:0,length:1
    I/loadWithSax: char:
                   ,ASCII:10
    I/loadWithSax: characters,start:0,length:2
    I/loadWithSax: char:    ,ASCII:9
    I/loadWithSax: char:    ,ASCII:9
    I/loadWithSax: startElement,uri:,localName:name,qName:name
    I/loadWithSax: characters,start:0,length:8
    I/loadWithSax: char:z,ASCII:122
    I/loadWithSax: char:h,ASCII:104
    I/loadWithSax: char:a,ASCII:97
    I/loadWithSax: char:n,ASCII:110
    I/loadWithSax: char:g,ASCII:103
    I/loadWithSax: char:s,ASCII:115
    I/loadWithSax: char:a,ASCII:97
    I/loadWithSax: char:n,ASCII:110
    I/loadWithSax: endElement,uri:,localName:name,qName:name
    
    • startDocument,开始解析xml
    • 解析到第一个标签的开始:<persons>
    • 然后解析到了内容???characters?按照我上面的分析,<persons>标签内没有文字内容,应该不会回调。其实,这里回调的是换行符。log中打出了ASCII码,10就是换行。然后,还有一个tab符。
    • 然后就是<persons>里面的<person>,有三个参数:id,key,type,巴拉巴拉
    • 。。。

    3.保存

    • sax的保存有点麻烦。具体是XmlSerializer的使用。

    • 初始化一个XmlSerializer:

    StringWriter stringWriter = new StringWriter();
    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    XmlSerializer xmlSerializer = factory.newSerializer();
    xmlSerializer.setOutput(stringWriter);
    
    • 声明文档的开始和结束:
    xmlSerializer.startDocument("utf-8", false);//false,是声明:standalone的值。
    xmlSerializer.endDocument();
    
    • 标签的开始结束,和写入内容:
    xmlSerializer.startTag(null, "name");//开始,第一个参数是namespace,命名空间。
    xmlSerializer.text(person.name);//写入内容
    xmlSerializer.endTag(null, "name");
    
    • 实战:
      假如,我们需要构建如下的XML:
    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <persons>
        <person id="1" key="33" type="type">
            <name>zhangsan</name>
            <age>21</age>
        </person>
        <person>
            <name>lisi</name>
            <age>12</age>
        </person>
        <person>
            <name>wangwu</name>
            <age>23</age>
        </person>
    </persons>
    
    • 首先你得定义好一个Bean类,Person:
    public class Person {
    
        public int id = -1;
        public String key = null;
        public String type = null;
        public String name;
        public int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
    • 然后开撸:最后的stringWriter就是你想要的数据,注意就是,一些换行和tab符。
    StringWriter stringWriter = new StringWriter();
    try {
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        XmlSerializer xmlSerializer = factory.newSerializer();
        xmlSerializer.setOutput(stringWriter);
    
        //制造假数据:
        ArrayList<Person> personArrayList = new ArrayList<>();
        Person person1 = new Person("zhangsan",21);
        person1.id=1;
        person1.key="33";
        person1.type="type";
        Person person2 = new Person("lisi",12);
        Person person3 = new Person("wangwu",23);
        personArrayList.add(person1);
        personArrayList.add(person2);
        personArrayList.add(person3);
    
        //star document
        xmlSerializer.startDocument("utf-8", true);
        xmlSerializer.text("\n");
        xmlSerializer.startTag(null, "persons");
    
        for(Person person:personArrayList){
            //star tag
            xmlSerializer.text("\n");
            xmlSerializer.text("\t");
            xmlSerializer.startTag(null, "person");
    
            //添加参数
            if (person.id!=-1) {
                xmlSerializer.attribute(null,"id",String.valueOf(person.id));
            }
            if (person.key!=null) {
                xmlSerializer.attribute(null,"key",person.key);
            }
            if (person.type!=null) {
                xmlSerializer.attribute(null,"type",person.type);
            }
    
            //添加内容:name
            xmlSerializer.text("\n");
            xmlSerializer.text("\t");
            xmlSerializer.text("\t");
            xmlSerializer.startTag(null, "name");
            xmlSerializer.text(person.name);
            xmlSerializer.endTag(null, "name");
    
            //添加内容:age
            xmlSerializer.text("\n");
            xmlSerializer.text("\t");
            xmlSerializer.text("\t");
            xmlSerializer.startTag(null, "age");
            xmlSerializer.text(String.valueOf(person.age));
            xmlSerializer.endTag(null, "age");
    
            //end tag
            xmlSerializer.text("\n");
            xmlSerializer.text("\t");
            xmlSerializer.endTag(null, "person");
        }
    
        //end document
        xmlSerializer.text("\n");
        xmlSerializer.endTag(null, "persons");
        xmlSerializer.endDocument();
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    • XmlSerializer的初始化需要传入一个write对象,你可以传入一个FileWrite,写到文件里面:
    // 创建文件对象
    File fileText = new File(saveFilePath);
    // 向文件写入对象写入信息
    FileWriter stringWriter;
    xmlSerializer.setOutput(stringWriter);
    //...同上
    //记得close
    if (stringWriter != null) {
        stringWriter.close();
    }
    

    4.增删

    • 增加和删除,那么你需要先对XML进行映射,映射成一堆的Bean,然后增加删除Bean,再保存即可。

    相关文章

      网友评论

        本文标题:Android-使用SAX对XML进行增删改查

        本文链接:https://www.haomeiwen.com/subject/logloxtx.html