XML,可扩展标记语言。可以用来存储数据,可以看做是一个小型的数据库,SharedPreference就是使用XML文件存储数据的,SQLite底层也是一个XML文件,而在网络应用方面,通常作为信息的载体,通常把数据包装成XML来传递。

1
2
3
4
5
6
7
8
9
10
11
12
      -----文档开始
<persons> -----开始元素(persons)
<person id = "11"> -----文本节点(空白文本) 开始元素(person)属性
<name>Coder-pig</name> -----文本节点(空白文本) 开始元素(name)属性 结束元素
<age>18</age> -----文本节点(空白文本) 开始元素(age)属性 结束元素
</person> -----文本节点(空白文本) 结束元素
<person id = "13">
<name>Jay</name>
<age>20</age>
</person>
</persons> -----结束元素(persons)
-----文档结束

上面就简单的定义了一个存储person对象的xml文件的编码,注意,外面的空白区域也是文本节点。

2. 三种解析XML方法的比较

2.1 SAX解析XML

对文档进行顺序扫描,当扫描到文档(doucument)开始与结束、元素(element)开始与结束等地方时,通知事件处理函数,由事件处理函数做相对应动作,然后继续进行同样的扫描,直至文档结束。解释速度快,占用内存小,,每需要解析一类XML,就需要编写新的适合该类的XML处理类,比较麻烦。采用的是流式解析,解析是同步的,读到哪就处理到哪。

2.2 Dom解析XML

先把XML文档读取到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存,假如读取的数据量大,手机内存不够的话,可能导致手机死机。不建议在Android设备中使用,解析简单的XML可以。常用的五个接口与类:DocculemElementNodeNodeListDOMParser,Don是整个文件解析到内存中,供用户需要的节点信息,支持随机访问。

2.3 pull解析XML

XML pull提供了开始元素和结束元素。当某个元素开始时,可以调用parsernextText从XML文档中提取所有字符数据。当解析到文档结束时,自动生成EndDocument。常用接口和类:XmlPullParserXmlSerializerXmlPullParserFactory。和SAX差不多,代码实现比较简单,非常适合移动设备,Android系统内置pull解析器,而且Android系统内部默认使用pull来解析XML文件。

3. SAX解析XML

SAX是一个解析速度快且占用内存少的XML解析器,非常适合用于Android等移动设备;SAX解析XML文件采用的是事件驱动,也就是说不需要解析整个文档,而是在解析过程中,判断读取的字符是否符合XML语法的某部分(文档开头,文档结束,或者标签开头和标签结束),符合的话就会触发事件(回调方法),而这些方法都定义在ContentHandler接口中,而ContentHandler是一个接口, 使用起来不方便,所以Android准备了一个帮助类DefaultHandler,只需要继承这个类,重写里面对应的方法即可。

可以重写的方法:

  • startDocument():当读取到文文档开始标志时触发,通常在这里完成一些初始化操作。
  • endDocument():文档结束部分,在这里完成一些善后工作。
  • startElement(names, paceURI, localName, qName, atts):参数依次问命名空间,不带命名空间的前缀标签名,带命名空间的前缀标签名,通过atts可以得到所有的属性名和相应的值;SAX中一个重要的特点就是它的流式处理,当遇到一个标签时,它并不会记录下以前遇到的标签,就是说,在startElement()中,所有知道的信息就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元素等其他与结构相关的信息,都不知道,需要程序来完成,这使得SAX在编程处理上没有DOM方便。
  • endElement(uri, localName, name):在遇到结束标签的时候,调用该方法。
  • characters(ch, start, length):这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch, start, length)就可以获取内容。

核心代码:SAX解析类——SaxHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package com.ly.allendemowebservice;

import android.util.Log;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; * SAX解析XML
*
* @author Liuyang
* @date 2019/7/26
*/
public class extends DefaultHandler {
private static final String TAG = "SaxHelper"; private Person mPerson;
private ArrayList<Person> mPersons; * 当前解析的元素标签
*/
private String mTagName = null; * 当读取到文档开始标志时触发,通常在里面完成一些初始化操作
*/
@Override
public void startDocument() throws SAXException {
this.mPersons = new ArrayList<>();
Log.i(TAG, "读取到文档头,开始解析xml");
} private static final String ELEMENT = "person"; * 读到一个开始标签时触发,第二个参数为标签名,最后一个参数为属性数组
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (ELEMENT.equals(localName)) {
mPerson = new Person();
mPerson.setId(Integer.parseInt(attributes.getValue("id")));
Log.i(TAG, "开始处理person元素~");
}
mTagName = localName;
} private static final String TAG_NAME1 = "name";
private static final String TAG_NAME2 = "age"; * 读取标签里面的内容,第一个参数为字符串内容,后面依次为起始位置和长度
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 判断当前标签是否有效
if (mTagName != null) {
String data = new String(ch, start, length);
// 读取标签内容
if (TAG_NAME1.equals(mTagName)) {
mPerson.setName(data);
Log.i(TAG, "处理name元素内容");
} else if (TAG_NAME2.equals(mTagName)) {
mPerson.setAge(Integer.parseInt(data));
Log.i(TAG, "处理age元素内容");
}
}
} * 读取到元素结束时触发,这里将对象添加到集合中
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (ELEMENT.equals(localName)) {
mPersons.add(mPerson);
mPerson = null;
Log.i(TAG, "处理person元素结束~");
}
mTagName = null;
} * 读取到文档结束时触发,
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
Log.i(TAG, "处理person元素结束~");
} * 获取persons集合
*/
public ArrayList<Person> getPersons() {
return mPersons;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private ArrayList<Person> readXmlForSax() throws IOException, ParserConfigurationException, SAXException {
// 获取文件资源建立输入流对象,可能是从网络获取
InputStream inputStream = getAssets().open("person1.xml");
// 创建XML解析器
SaxHelper helper = new SaxHelper();
// 得到SAX解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 创建SAX解析器
SAXParser parser = factory.newSAXParser();
// 将XML解析器分配给解析器,对文档进行解析,将事件发送给处理器
parser.parse(inputStream, helper);
inputStream.close();
return helper.getPersons();
}

在项目assets目录下有一个文件person1.xml

1
2
3
4
5
6
7
8
9
10
11
<persons>
<person id = "11">
<name>SAX解析</name>
<age>18</age>
</person>
<person id = "13">
<name>XML1</name>
<age>43</age>
</person>
</persons>

Demo地址:Allen_Demo_WebService

4. DOM解析XML数据

DOM解析XML文件时会将文件所有的内容以文档树的形式存放在内存中,可以使用DOM API遍历XML树,检索到需要的数据。使用DOM操作XML的代码比较直观,并且在编码方面比基于SAX的实现更加简单。但是DOM需要将XML文件的所有内容存放到内存中,所以内存消耗大,特别是对于Android设备而言,内存资源有限,因此建议使用前面的SAX解析。如果解析的内容比较小,也可以使用DOM来解析。

DOM API:

  • DocumentBuilderFactory(解析器工厂类):创建方法DoucmentBuilderFactory factory = DoucmentBuilderFactory.newInstance();

  • DocumentBuilder(解析器类):创建方法:通过解析器工厂类来获得DocumentBuidler builder = factory.newDocumentBuilder();

  • Document(文档树模型):将需要解析的XML文件读入DOM解析器:Document doc = builder.parse(context.getAssets().open("person2.xml"));

    Document对象代表了一个XML文档的模型,所有的其他Node都以一定的顺序包含在Document对象内,排列成树状,以后对XML文档的所有操作都与解析器无关。

  • NodeList(列表类):代表一个包含一个或多个Node的列表,有以下两个方法:

    • item(index):返回集合的第indexNode项;
    • getLength():列表的节点数
  • Node(节点类):DOM中最基本的对象,代表文档树中的抽象节点,很少会直接使用;通常调用其子对象的ElementAttrText等。

  • Element(元素类):Node最主要的子对象,在元素中可以包含属性,因此有获取属性的方法:

    • getAttrbute():获取属性值
    • getTagName():获取元素名称
  • Attr(属性类):代表某个元素的属性,虽然Attr继承自Node接口,但因为Attr是包含在Element中的,但并不能将其看做是Element的子对象,因为Attr并不是DOM树的一部分。

核心代码:DOM解析类——DomHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
大专栏  二、Android XML数据解析">64
65
66
67
68
69
70
71
72
73
74
75
76
package com.ly.allendemowebservice;

import android.content.Context;
import android.util.Log; import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import java.io.IOException;
import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; * DOM解析XML
*
* @author Liuyang
* @date 2019/7/26
*/
public class DomHelper {
private static final String TAG = "DomHelper";
private static final String TAG_NAME = "name";
private static final String TAG_AGE = "age"; public static ArrayList<Person> queryXML(Context context) {
ArrayList<Person> persons = new ArrayList<>();
try {
// 1. 获取DOM解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 获取DOM解析器
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 将要解析的XML文件读入DOM解析器
Document document = builder.parse(context.getAssets().open("person2.xml")); Log.i(TAG, "处理该文档的DomImplementation对象 = " + document.getImplementation()); // 4. 得到文档中名称为person的元素的节点元素
NodeList nodeList = document.getElementsByTagName("person");
// 5. 遍历集合,显示集合中的元素以及子元素的名字
for (int i = 0; i < nodeList.getLength(); i++) {
// 先从person元素开始解析
Element personElement = (Element) nodeList.item(i);
Person person = new Person();
person.setId(Integer.valueOf(personElement.getAttribute("id"))); // 获取person下的name和age的Note集合
NodeList childNodeList = personElement.getChildNodes();
for (int j = 0; j < childNodeList.getLength(); j++) {
Node childNode = childNodeList.item(j);
// 判断子Node类型是否为元素的Node
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) childNode;
if (TAG_NAME.equals(childElement.getNodeName())) {
person.setName(childElement.getFirstChild().getNodeValue());
} else if (TAG_AGE.equals(childElement.getNodeName())) {
person.setAge(Integer.valueOf(childElement.getFirstChild().getNodeValue()));
}
}
}
persons.add(person);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return persons;
}
}

5. PULL解析XML数据

除了SAX和DOM解析XML之外,Android系统内置了Pull解析器用来解析XML,比如SharedPreference就是使用内置的pull解析配置文件的。它的使用和SAX类似,都是采用事件驱动来完成XML的解析,而pull代码比较简单,只需处理开始和结束的事件,通常使用switch语句,根据事件不同的类型,匹配不同的处理方式,有五种事件:START_DOCUMENTSTART_TAGTEXTEND_TAGEND_DUCOMENT

XML pull 提供了开始元素和结束元素。当某个元素开始的时候,可以调用paser.nextText从XML文档中提取所有字符数据。当解析到一个文档结束时,自动生成EndDocument事件。在PULL解析过程中返回的是数字,且需要自己获取产生事件然后做出相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码:读取到XML的声明返回START_DOCUMENT;结束返回END_DOCUMENT;开始标签返回START_TAG;结束标签返回END_TAG;文本返回TEXT
使用PULL解析XML的流程:

  1. 获取一个XmlPullPaser类的引用:

    1
    2
    3
    4
    // 方式1:通过xml解析工厂获得实体类
    XmlPullPaserFactory factory = XmlPullPaserFactory.newInstance(); XmlPullPaser paser = factory.newPullPaser();
    // 方式2:直接获得实体类
    XmlPullPaser paser = Xml.newPullPaser();
  2. paser解析器对象提供xml流与编码格式:

    1
    paser.setInput(xml, "UTF-8");
  3. 获得事件的类型:

    1
    int eventType = paser.getEventType();
  4. switch对不同的事件类型进行不同的处理:

  • START_DUCUMENT:开始读文档时触发,在这里完成初始化操作;
  • START_TAG:开始读标签,通过pasergetName()方法获得标签名信息比较,使用getAttributeValue(index)获取属性值;
  • 对于文字节点TEXT可以使用paser.nextText()获得节点内容;
  • END_TAG:标签结束;
  • paser.next():循环解析下一个元素。

核心代码:PULL解析XML——PullHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.ly.allendemowebservice;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; * PULL解析XML
*
* @author Liuyang
* @date 2019/7/27
*/
public class PullHelper {
public static ArrayList<Person> getPersons(InputStream xml) throws XmlPullParserException, IOException {
ArrayList<Person> persons = null;
Person person = null;
// 创建一个XML解析工厂
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
// 获得XML解析类的引用
XmlPullParser parser = factory.newPullParser();
parser.setInput(xml, "UTF_8");
// 获得事件类型
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
persons = new ArrayList<>();
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();
assert person != null;
person.setName(name);
} else if ("age".equals(parser.getName())) {
int age = Integer.parseInt(parser.nextText());
assert person != null;
person.setAge(age);
}
break;
case XmlPullParser.END_TAG:
if ("person".equals(parser.getName())) {
assert persons != null;
persons.add(person);
person = null;
}
break;
default:
break;
}
eventType = parser.next();
}
return persons;
}
}

使用:

1
2
3
4
5
6
7
8
9
10
11
try {
InputStream inputStream = getAssets().open("person3.xml");
ArrayList<Person> persons = PullHelper.getPersons(inputStream);
for (int i = 0; i < persons.size(); i++) {
Log.i(TAG, i + " == " + persons.get(i).toString());
}
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}

使用PULL生成XML数据的流程:

  1. 创建XMlSerializer(XML序列化类)的实例:

    1
    XmlSerializer serializer = Xml.newSerializer();
  2. XmlSerializer设置输出流与编码格式:

    1
    serializersetOutput(out, "UTF-8");
  3. XMlSerializer设置XML的编码格式:

    1
    serializer.startDocument("UTF-8", true);
  4. 设置根元素:

    1
    serializer.startTag(null, "person");
  5. 使用foreach循环遍历persons集合中所有的元素,同时依次写入标签与属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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");
    }
  6. 设置跟踪完结元素:

    1
    serializer.endTag(null, "persons");
  7. 结束文档编写:

    1
    serializer.endDocument();
  8. 调用flush(),将内存中的数据写入文件中并关闭输出流

    1
    2
    out.flush();
    out.close();

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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();
}

Demo地址:Allen_Demo_WebService

二、Android XML数据解析的更多相关文章

  1. Android XML数据解析

    XML:可扩展标记语言.一般用于数据存储,SharedPreference就是使用xml文件保存信息的,SQLite底层也是xml文件,在网络方面通常作为信息的载体,把数据包装成xml来传递. XML ...

  2. iOS开发——网络Swift篇&JSON与XML数据解析

    JSON与XML数据解析 JSON数据解析(内置NSJSONSerialization与第三方JSONKit)   一,使用自带的NSJSONSerialization 苹果从IOS5.0后推出了SD ...

  3. xml数据解析

    xml数据解析 在iPhone开发中,XML的解析有很多选择,iOS SDK提供了NSXMLParser和libxml2两个类库,另外还有很多第三方类库可选,例如TBXML.TouchXML.Kiss ...

  4. iOS - XML 数据解析

    前言 @interface NSXMLParser : NSObject public class NSXMLParser : NSObject 1.XML 数据 XML(Extensible Mar ...

  5. xml数据解析调研

    XML数据解析http://www.tuicool.com/articles/Nraau2(必用) http://www.cnblogs.com/pengyingh/articles/2342699. ...

  6. iOS开发网络篇之Web Service和XML数据解析

    郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠,支持郝萌主.捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 游戏官方下 ...

  7. 关于C#对Xml数据解析

    首先进行简单说明Xml 与Html  和 XAML数据标签的差别. 1.Xml属于数据文本, 被设计为传输和存储数据,其焦点是数据的内容.它与json格式数据相似,可作为服务数据传输类型. 其中XML ...

  8. Android 之XML数据解析(2)—— SAX解析

    (以下文章基本照抄郭霖大神的<第一行代码>) 在Android之 解析XML文件(1)—— Pull解析 中我们讲了Pull方式解析XML文件.今天讲另外一种方式,SAX解析XML文件. ...

  9. Android json 数据解析

    1.json格式 2.json解析 3.gson解析 4.fastjson解析 一.Json格式 json一种轻量级的数据交换格式.在网络上传输交换数据一般用xml, json. 两种结构: 1)对象 ...

随机推荐

  1. 17.3.12---xmlrpclib模块

    1----XML-RPC是一种使用xml文本的方式利用http协议传输命令和数据的rpc基址,我们用pythom的想mlrpclib模块可以让程序与其他任何语言编写的XML-RPC服务器进行数据传输 ...

  2. python-day3爬虫基础之下载网页

    今天主要学习了关于网页下载器的一些内容,下边做一下总结: 1.网页下载器,顾名思义,就是将URL所对应的网页以HTML的形式下载到本地,最终存储成本地文件或者还是本地内存字符串,然后进行后续的分析与处 ...

  3. QeePHP

    百度百科: https://baike.baidu.com/item/qeephp/8328612?fr=aladdin 官方地址: http://www.qeephp.cn/app/index.ph ...

  4. voxelmorph配置

    简介 VoxelMorph使用CNN实现了非监督的医学图像配准,速度较之前的方法有很大提升.主要特点有: 提出了一种基于学习的解决方案,不需要在训练过程中获取诸如ground truth对应或解剖标志 ...

  5. PAT甲级——1006 Sign In and Sign Out

    PATA1006 Sign In and Sign Out At the beginning of every day, the first person who signs in the compu ...

  6. Linux安装svn服务

    安装svn yum -y install subversion 创建保存仓库/版本库的目录 mkdir -p /opt/data/svndir 创建仓库/版本库, 这里同时创建两个仓库(project ...

  7. 《C程序设计语言》练习1-10

    #include<stdio.h> main() { int c; c=getchar(); while (c !=EOF) { if (c=='\t') { c='\\'; putcha ...

  8. linux查看并发连接数

    1.查看TCP的并发请求数及其TCP连接状态: netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' netstat ...

  9. Django学习之视图层

    视图层 小白必会三板斧 HttpResponse render redirect django视图函数必须要给返回一个HttpResponse对象(render和redirect内部返回的也是一个Ht ...

  10. 79)PHP,session函数编写的注意事项

    (1)先执行  session_set_save_handler()  在session_start(). (2)那么开启session_start(),有两种方法,一个就是session_start ...