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可以。常用的五个接口与类:Docculem
、Element
、Node
、NodeList
、DOMParser
,Don是整个文件解析到内存中,供用户需要的节点信息,支持随机访问。
2.3 pull解析XML
XML pull提供了开始元素和结束元素。当某个元素开始时,可以调用parser
、nextText
从XML文档中提取所有字符数据。当解析到文档结束时,自动生成EndDocument
。常用接口和类:XmlPullParser
、XmlSerializer
、XmlPullParserFactory
。和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;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元素结束~" ); } 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" ); SaxHelper helper = new SaxHelper(); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); 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)
:返回集合的第index
个Node
项;
getLength()
:列表的节点数
Node
(节点类):DOM中最基本的对象,代表文档树中的抽象节点,很少会直接使用;通常调用其子对象的Element
、Attr
、Text
等。
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;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 { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(context.getAssets().open("person2.xml" )); Log.i(TAG, "处理该文档的DomImplementation对象 = " + document.getImplementation()); NodeList nodeList = document.getElementsByTagName("person" ); for (int i = 0 ; i < nodeList.getLength(); i++) { Element personElement = (Element) nodeList.item(i); Person person = new Person(); person.setId(Integer.valueOf(personElement.getAttribute("id" ))); NodeList childNodeList = personElement.getChildNodes(); for (int j = 0 ; j < childNodeList.getLength(); j++) { Node childNode = childNodeList.item(j); 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_DOCUMENT
、START_TAG
、TEXT
、END_TAG
、END_DUCOMENT
。
XML pull 提供了开始元素和结束元素。当某个元素开始的时候,可以调用paser.nextText
从XML文档中提取所有字符数据。当解析到一个文档结束时,自动生成EndDocument
事件。在PULL解析过程中返回的是数字,且需要自己获取产生事件然后做出相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码:读取到XML的声明返回START_DOCUMENT
;结束返回END_DOCUMENT
;开始标签返回START_TAG
;结束标签返回END_TAG
;文本返回TEXT
。 使用PULL解析XML的流程:
获取一个XmlPullPaser
类的引用:
1 2 3 4
XmlPullPaserFactory factory = XmlPullPaserFactory.newInstance(); XmlPullPaser paser = factory.newPullPaser(); XmlPullPaser paser = Xml.newPullPaser();
为paser
解析器对象提供xml流与编码格式:
1
paser.setInput(xml, "UTF-8" );
获得事件的类型:
1
int eventType = paser.getEventType();
用switch
对不同的事件类型进行不同的处理:
START_DUCUMENT
:开始读文档时触发,在这里完成初始化操作;
START_TAG
:开始读标签,通过paser
的getName()
方法获得标签名信息比较,使用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;public class PullHelper { public static ArrayList<Person> getPersons (InputStream xml) throws XmlPullParserException, IOException { ArrayList<Person> persons = null ; Person person = null ; XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 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数据的流程:
创建XMlSerializer
(XML序列化类)的实例:
1
XmlSerializer serializer = Xml.newSerializer();
为XmlSerializer
设置输出流与编码格式:
1
serializersetOutput(out, "UTF-8" );
为XMlSerializer
设置XML的编码格式:
1
serializer.startDocument("UTF-8" , true );
设置根元素:
1
serializer.startTag(null , "person" );
使用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" ); }
设置跟踪完结元素:
1
serializer.endTag(null , "persons" );
结束文档编写:
1
serializer.endDocument();
调用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数据解析