XML在各种开发中都广泛应用,Android也不例外。作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能。今天就由我向大家介绍一下在Android平台下几种常见的XML解析和创建的方法。

在Android中,常见的XML解析器分别为SAX解析器、DOM解析器和PULL解析器,下面,我将一一向大家详细介绍。

SAX解析器:

SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。

SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

DOM解析器:

DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。

由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。

PULL解析器:

PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器。

以上三种解析器,都是非常实用的解析器,我将会一一介绍。我们将会使用这三种解析技术完成一项共同的任务。

我们新建一个项目,项目结构如下:

Android中解析XML 技术详解---->SAX解析、DOM解析、PULL解析_第1张图片" width="305" height="532" style="border:1px solid black;">

我在项目的src目录中放置一个XML文档persons.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?><persons><person id="1"><name>sax</name><age>30</age></person><person id="2"><name>dom</name><age>40</age></person><person id="3"><name>pull</name><age>50</age></person><person id="4"><name>ouyangpeng</name><age>60</age></person><person id="5"><name>chengming</name><age>70</age></person><person id="6"><name>just for test</name><age>80</age></person></persons>

然后我们分别使用以上三种解析技术解析文档,得到一个List<Person>的对象,先来看一下Person.java的代码:

package cn.roco.xml.domain;public class Person {private Integer id;private String name;private Integer age;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Override     public String toString() {         return "id:" + id + ", name:" + name + ", age:" + age;     }  }


接下来,就该介绍操作过程了,我们先为解析器定义一个IPersonService接口,每种类型的解析器需要实现此接口。IPersonService.java代码如下:
package cn.roco.xml.service;import java.io.InputStream;import java.io.OutputStream;import java.util.List;import cn.roco.xml.domain.Person;public interface IPersonService {/** * 获取数据 * @param is  输入方向  * @return   数据 * @throws Exception */public List<Person> parse(InputStream is) throws Exception;/** * 保存数据 * @param persons  数据 * @param out  输出方向 * @throws Exception */public void serialize(List<Person> persons, OutputStream out)throws Exception;}

好了,我们就该一个一个的实现该接口,完成我们的解析过程。

使用SAX解析器:

PersonServiceImpBySax.java代码如下:

package cn.roco.xml.service.imp;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import javax.xml.transform.OutputKeys;import javax.xml.transform.Result;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerFactory;import javax.xml.transform.sax.SAXTransformerFactory;import javax.xml.transform.sax.TransformerHandler;import javax.xml.transform.stream.StreamResult;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.AttributesImpl;import org.xml.sax.helpers.DefaultHandler;import cn.roco.xml.domain.Person;import cn.roco.xml.service.IPersonService;public class PersonServiceImpBySax implements IPersonService {@Overridepublic void serialize(List<Person> persons, OutputStream out)throws Exception {SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();// 取得SAXTransformerFactory实例TransformerHandler handler = factory.newTransformerHandler(); // 从factory获取TransformerHandler实例Transformer transformer = handler.getTransformer(); // 从handler获取Transformer实例transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // 设置输出采用的编码方式transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 是否自动添加额外的空白transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 是否忽略XML声明Result result=new StreamResult(out);handler.setResult(result);String uri = ""; // 代表命名空间的URI 当URI无值时 须置为空字符串String localName = ""; // 命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串handler.startDocument();handler.startElement(uri, localName, "persons", null);AttributesImpl attrs = new AttributesImpl(); // 负责存放元素的属性信息char[] ch = null;for (Person person : persons) {attrs.clear();// 清空属性列表attrs.addAttribute(uri, localName, "id", "string",String.valueOf(person.getId()));// 添加一个名为id的属性(type影响不大,这里设为string)handler.startElement(uri, localName, "person", attrs); // 开始一个person元素// 关联上面设定的id属性handler.startElement(uri, localName, "name", null); // 开始一个name元素   没有属性ch = String.valueOf(person.getName()).toCharArray();handler.characters(ch, 0, ch.length); // 设置name元素的文本节点handler.endElement(uri, localName, "name");handler.startElement(uri, localName, "age", null); // 开始一个name元素   没有属性ch = String.valueOf(person.getAge()).toCharArray();handler.characters(ch, 0, ch.length); // 设置name元素的文本节点handler.endElement(uri, localName, "age");handler.endElement(uri, localName, "person");}handler.endElement(uri, localName, "persons");handler.endDocument();}@Overridepublic List<Person> parse(InputStream is) throws Exception {SAXParserFactory factory = SAXParserFactory.newInstance();// 取得SAXParserFactory实例SAXParser saxParser = factory.newSAXParser(); // 从factory获取SAXParser实例MyHandler handler = new MyHandler();// 实例化自定义HandlersaxParser.parse(is, handler); // 根据自定义Handler规则解析输入流return handler.getPersons();}private final class MyHandler extends DefaultHandler {private List<Person> persons;private Person person;private StringBuilder builder;public List<Person> getPersons() {return persons;}@Overridepublic void startDocument() throws SAXException {super.startDocument();persons = new ArrayList<Person>();builder = new StringBuilder();}@Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {super.startElement(uri, localName, qName, attributes);if ("person".equals(localName)) {person = new Person();Integer id = Integer.parseInt(attributes.getValue(0));person.setId(id);}builder.setLength(0);// 将字符长度设置为0 以便重新开始读取元素内的字符节点}@Overridepublic void characters(char[] ch, int start, int length)throws SAXException {super.characters(ch, start, length);builder.append(ch, start, length); // 将读取的字符数组追加到builder中}@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {super.endElement(uri, localName, qName);if ("name".equals(localName)) {person.setName(builder.toString());} else if ("age".equals(localName)) {person.setAge(Integer.parseInt(builder.toString()));} else if ("person".equals(localName)) {persons.add(person);}}}}


代码中,我们定义了自己的事件处理逻辑,重写了DefaultHandler的几个重要的事件方法。下面我为大家着重介绍一下DefaultHandler的相关知识。DefaultHandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据。它实现了EntityResolver接口、DTDHandler接口、ErrorHandler接口和ContentHandler接口。这几个接口代表不同类型的事件处理器。我们着重介绍一下ContentHandler接口。结构如图:

Android中解析XML 技术详解---->SAX解析、DOM解析、PULL解析_第2张图片" width="388" height="284" style="border:1px solid black;">

这几个比较重要的方法已被我用红线标注,DefaultHandler实现了这些方法,但在方法体内没有做任何事情,因此我们在使用时必须覆写相关的方法。最重要的是startElement方法、characters方法和endElement方法。当执行文档时遇到起始节点,startElement方法将会被调用,我们可以获取起始节点相关信息;然后characters方法被调用,我们可以获取节点内的文本信息;最后endElement方法被调用,我们可以做收尾的相关操作。

最后,我们需要调用SAX解析程序,这个步骤在MainActivity中完成:

package cn.roco.xml;import java.io.FileOutputStream;import java.io.InputStream;import java.util.List;import cn.roco.xml.domain.Person;import cn.roco.xml.service.IPersonService;import cn.roco.xml.service.imp.PersonServiceImpByDom;import cn.roco.xml.service.imp.PersonServiceImpByPull;import cn.roco.xml.service.imp.PersonServiceImpBySax;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity {TextView display;List<Person> persons = null;IPersonService personService = null;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// 指定IPersonService由PersonServiceImpByPull实现// personService = new PersonServiceImpByPull();// 指定IPersonService由PersonServiceImpBySax实现personService = new PersonServiceImpBySax();// 指定IPersonService由PersonServiceImpByDom实现//personService = new PersonServiceImpByDom();Button readButton = (Button) findViewById(R.id.readButton);Button saveButton = (Button) findViewById(R.id.saveButton);display = (TextView) findViewById(R.id.display);readButton.setOnClickListener(new ReadButtonOnClickListener());saveButton.setOnClickListener(new SaveButtonOnClickListener());}private final class SaveButtonOnClickListener implementsView.OnClickListener {@Overridepublic void onClick(View view) {try {FileOutputStream out = openFileOutput("persons_backup.xml",Context.MODE_PRIVATE);personService.serialize(persons, out);Toast.makeText(getApplicationContext(), R.string.save_succ, 1).show();} catch (Exception e) {Toast.makeText(getApplicationContext(), R.string.save_fail, 1).show();e.printStackTrace();}}}private final class ReadButtonOnClickListener implementsView.OnClickListener {@Overridepublic void onClick(View v) {InputStream xml = this.getClass().getClassLoader().getResourceAsStream("person.xml");try {persons = personService.parse(xml);} catch (Exception e) {Toast.makeText(getApplicationContext(), R.string.read_fail, 1).show();e.printStackTrace();}display.setText("");for (Person person : persons) {display.append(person.toString() + "\n");}}}}


界面就两个按钮,顺便给大家贴上:

Android中解析XML 技术详解---->SAX解析、DOM解析、PULL解析_第3张图片" width="371" height="538" style="border:1px solid black;">

点击“readXML”按钮,将会调用SAX解析器解析文档,并在TextView中显示相关信息,如下图:

Android中解析XML 技术详解---->SAX解析、DOM解析、PULL解析_第4张图片" width="379" height="533" style="border:1px solid black;">

然后再点击“保存数据”按钮,将会在该应用包下的files目录生成一个person_backup.xml文件:

Android中解析XML 技术详解---->SAX解析、DOM解析、PULL解析_第5张图片" width="650" height="276" style="border:1px solid black;">

将persons_backup.xml文件导出到桌面上,格式化后如图所示:

Android中解析XML 技术详解---->SAX解析、DOM解析、PULL解析_第6张图片" width="445" height="486" style="border:1px solid black;">

使用DOM解析器:

PersonServiceImpByDom.java代码如下:

package cn.roco.xml.service.imp;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import javax.xml.transform.OutputKeys;import javax.xml.transform.Result;import javax.xml.transform.Source;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.sax.SAXTransformerFactory;import javax.xml.transform.sax.TransformerHandler;import javax.xml.transform.stream.StreamResult;import org.w3c.dom.Attr;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.AttributesImpl;import org.xml.sax.helpers.DefaultHandler;import android.R.bool;import cn.roco.xml.domain.Person;import cn.roco.xml.service.IPersonService;public class PersonServiceImpByDom implements IPersonService {@Overridepublic void serialize(List<Person> persons, OutputStream out)throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document doc = builder.newDocument(); // 由builder创建新文档Element rootElement = doc.createElement("persons");for (Person person : persons) {Element personElement = doc.createElement("person");Attr id=doc.createAttribute("id"); //创建Id属性节点id.setValue( person.getId().toString()); //给属性赋值personElement.setAttributeNode(id);//把id属性节点追加到personElement nameElement = doc.createElement("name");nameElement.setTextContent(person.getName());personElement.appendChild(nameElement);Element ageElement = doc.createElement("age");ageElement.setTextContent(person.getAge().toString());personElement.appendChild(ageElement);rootElement.appendChild(personElement);}doc.appendChild(rootElement);TransformerFactory transFactory = TransformerFactory.newInstance();// 取得TransformerFactory实例Transformer transformer = transFactory.newTransformer(); // 从transFactory获取Transformer实例transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // 设置输出采用的编码方式transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 是否自动添加额外的空白transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 是否忽略XML声明Source source=new DOMSource(doc);Result result=new StreamResult(out);transformer.transform(source, result);}@Overridepublic List<Person> parse(InputStream is) throws Exception {List<Person> persons = new ArrayList<Person>();DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 取得DocumentBuilderFactory实例DocumentBuilder builder = factory.newDocumentBuilder(); // 从factory获取DocumentBuilder实例Document doc = builder.parse(is); // 解析输入流 得到Document实例Element rootElement = doc.getDocumentElement();NodeList items = rootElement.getElementsByTagName("person");for (int i = 0; i < items.getLength(); i++) {Element item = (Element) items.item(i);Person person = new Person();person.setId(Integer.parseInt(item.getAttribute("id")));NodeList properties = item.getChildNodes();for (int j = 0; j < properties.getLength(); j++) {if (properties.item(j).getNodeType() == Node.ELEMENT_NODE) {Node property = properties.item(j);String nodeName = property.getNodeName();if ("name".equals(nodeName)) {person.setName(property.getFirstChild().getNodeValue());} else if ("age".equals(nodeName)) {person.setAge(Integer.parseInt(property.getFirstChild().getNodeValue()));}}}persons.add(person);}return persons;}}


然后再MainActivity中只需改一个地方:

// 指定IPersonService由PersonServiceImpByPull实现// //personService = new PersonServiceImpByPull();// 指定IPersonService由PersonServiceImpBySax实现////personService = new PersonServiceImpBySax();// 指定IPersonService由PersonServiceImpByDom实现personService = new PersonServiceImpByDom();

执行结果是一样的。

使用PULL解析器:

PersonServiceImpByPull.java代码如下:

package cn.roco.xml.service.imp;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlSerializer;import android.util.Xml;import cn.roco.xml.domain.Person;import cn.roco.xml.service.IPersonService;public class PersonServiceImpByPull implements IPersonService {/** * 保存数据 序列号 *  * @param persons *            数据 * @param out *            输出方向 * @throws Exception */public void serialize(List<Person> persons, OutputStream out)throws Exception {XmlSerializer serializer = Xml.newSerializer(); // 由android.util.Xml创建一个XmlSerializer实例serializer.setOutput(out, "UTF-8");serializer.startDocument("UTF-8", true);serializer.startTag(null, "persons");for (Person person : persons) {serializer.startTag(null, "person");serializer.attribute(null, "id", person.getId().toString());serializer.startTag(null, "name");serializer.text(person.getName());serializer.endTag(null, "name");serializer.startTag(null, "age");serializer.text(person.getAge().toString());serializer.endTag(null, "age");serializer.endTag(null, "person");}serializer.endTag(null, "persons");serializer.endDocument();out.flush();out.close();}/** * 获取数据 解析XML *  * @param is *            输入方向 * @return 数据 * @throws Exception */public List<Person> parse(InputStream is) throws Exception {List<Person> persons = null;Person person = null;// XmlPullParserFactory factory = XmlPullParserFactory.newInstance();// XmlPullParser pullParser = factory.newPullParser();XmlPullParser pullParser = Xml.newPullParser();// 由android.util.Xml创建一个XmlPullParser实例pullParser.setInput(is, "UTF-8");// 设置输入流 并指明编码方式int eventType = pullParser.getEventType();while (eventType != XmlPullParser.END_DOCUMENT) {switch (eventType) {case XmlPullParser.START_DOCUMENT:persons = new ArrayList<Person>();break;case XmlPullParser.START_TAG:if ("person".equals(pullParser.getName())) {person = new Person();person.setId(Integer.parseInt(pullParser.getAttributeValue(0)));}if ("name".equals(pullParser.getName())) {person.setName(pullParser.nextText());}if ("age".equals(pullParser.getName())) {person.setAge(Integer.parseInt(pullParser.nextText()));}break;case XmlPullParser.END_TAG:if ("person".equals(pullParser.getName())) {persons.add(person);person = null;}break;}eventType = pullParser.next();}return persons;}}


然后再对MainActivity做以下更改:

       // 指定IPersonService由PersonServiceImpByPull实现personService = new PersonServiceImpByPull();// 指定IPersonService由PersonServiceImpBySax实现// personService = new PersonServiceImpBySax();// 指定IPersonService由PersonServiceImpByDom实现// personService = new PersonServiceImpByDom();


和其他两个执行结果都一样。

对于这三种解析器各有优点,我个人比较倾向于PULL解析器,因为SAX解析器操作起来太笨重,DOM不适合文档较大,内存较小的场景,唯有PULL轻巧灵活,速度快,占用内存小,使用非常顺手。读者也可以根据自己的喜好选择相应的解析技术。


==================================================================================================

作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!

转载请保留原文地址:http://blog.csdn.net/ouyang_peng

==================================================================================================


更多相关文章

  1. Android热补丁技术—dexposed原理简析(阿里Hao)
  2. Android(安卓)Architecture LifeCycle
  3. Android(安卓)Wi-Fi Peer-to-Peer(Android的Wi-Fi P2P对等网络)
  4. Android(安卓)进阶 教你打造 Android(安卓)中的 IOC 框架 【View
  5. Android面试题(五)—— Android的消息机制
  6. Android(安卓)调用js,传对象到js里面使用addJavascriptInterface
  7. 源码解析Android中的事件处理
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)导入v7包常见错误,以及项目
  2. Android邮件附件中文名乱码问题草解
  3. android上传图片、视频、文件,服务端使用w
  4. Android启动模式——LaunchMode
  5. android操作sdcard中的多媒体文件
  6. Android关键资源详解
  7. Android(安卓)PC端截图源代码
  8. 拥抱Android
  9. Android界面设计中的.9.png的使用技巧
  10. Android应用程序注册广播接收器(registerR