Android利用SAX对XML进行增删改查操作详解
作者:栗子酱油饼 发布时间:2023-02-05 00:03:32
前言
解析XML的方式有很多种,大家比较熟悉的可能就是DOM解析。
DOM(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据DOM接口来操作这个树结构了。
优点:整个文档读入内存,方便操作:支持修改、删除和重现排列等多种功能。
缺点:将整个文档读入内存中,保留了过多的不需要的节点,浪费内存和空间。
使用场合:一旦读入文档,还需要多次对文档进行操作,并且在硬件资源充足的情况下(内存,CPU)。
为了解决DOM解析存在的问题,就出现了SAX解析。其特点为:
优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如android,极力推荐使用SAX解析。
缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。
使用场合:机器有性能限制。
本文将给大家详细介绍关于Android利用SAX对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,再保存即可。
来源:https://www.jianshu.com/p/64694c5ecb0a?


猜你喜欢
- 具体实现过程不多说了,直接贴代码了。using System;using System.Collections.Generic;using
- 前言本文给你提供在Spring Boot 应用程序中编写好的单元测试的机制,并且深入技术细节。我们将带你学习如何以可测试的方式创建Sprin
- 一. 线性表中的顺序表线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见
- @Autowired注解注入的xxxMapper报错项目场景Mybatis-Plus测试问题描述在Mybatis-Plus场景的测试中发现,
- 0.导入命名空间:using Microsoft.Office.Core;using Microsoft.Office.Interop.Ex
- Glide介绍Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管
- Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。Field 成员变量的介
- Feign传递请求头信息在我之前的文章服务网关Spring Cloud Zuul中,将用户的登录id放在了请求头中传递给内部服务。但是当内部
- 一、使用PinYinConverterCore获取汉语拼音最新在做一个搜索组件,需要使用汉语拼音的首字母查询出符合条件的物品名称,由于汉字存
- Zuul作为网关服务,是其他各服务对外中转站,通过Zuul进行请求转发。这就涉及到部分数据是不能原封返回的,比如服务之间通信的凭证,用户的加
- 本文实例为大家分享了Java测试网络连通性的方法,供大家参考,具体内容如下第一种方式:利用java运行时: Java代码 /** * tes
- 二维(多维)vector添加一个空项今天刷Leecode,需要创建一个二维vector,vector<vector < int
- 1.SpringBoot整合JDBCTemplate1.1.导入jdbc相关依赖包主要的依赖包:<dependency> &nb
- Flutter 键值存储数据库键值存储是开发中十分常见的需求,在Flutter开发中,一般使用 shared_preferences 插件来
- 1.普通轮询算 * 询(Round Robin,RR)是依次将用户的访问请求,按循环顺序分配到web服务节点上,从1开始到最后一台服务器节点结
- RocketMq消息处理RocketMq消息处理整个流程如下:本系列RocketMQ4.8注释github地址,希望对大家有所帮助,要是觉得
- 在Android开发中我们经常有这样的需求,从服务器上下载xml或者JSON类型的数据,其中包括一些图片资源,本demo模拟了这个需求,从网
- 我已经很精简了,两篇(Spring Boot启动过程(一)、spring Boot启动过程(二))依然没写完,接着来。refreshCont
- 一、Nuget安装log4net --> Install-Package log4net二、在AssemblyInfo.cs文件中添加
- 前言值类型和引用类型,是c#比较基础,也必须掌握的知识点,但是也不是那么轻易就能掌握,今天跟着老胡一起来看看吧。 典型类型首先我们