在Android中,实现对XML的解析有三种方式,分别为DOM解析器,SAX解析器和PULL解析器。
- DOM解析器
DOM是基于树形结构的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树,检索所需数据。
分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。
Android完全支持DOM解析。利用DOM中的对象,可以对XML文档进行读取,搜索,修改,添加和删除等操作。
DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素,属性和注释等,
然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档。
DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,
这样代码就可以使用DOM接口来操作整个树结构。
由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会
很耗资源。当然,如果XML文件的内容比较小,采用DOM是可行的。
常用的DOM接口和类:
Document: 该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。
Element: 该接口继承Node接口,提供了获取,修改XML元素名字和属性的方法。
Node: 该接口提供处理并获取节点和子节点值的方法。
NodeList: 提供获得节点个数和当前节点的方法。这样就可以迭代地访问各节点。
DOMParser: 该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。
示例:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="1">
<name>叶问</name>
<age>23</age>
</person>
<person id="2">
<name>李小龙</name>
<age>17</age>
</person>
</persons>
public class DomXMLReader {
public static List<Person> readXML( InputStream inStream) {
List<Person> persons = new ArrayList<Person>;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builer = factory.newDocumentBuilder();
Document dom = builder.parse(inStream);
Element root = dom.getDocumentElement();
NodeList items = root.getElementsByTagName("person");
//查找所有person节点
for (int i = 0; i < items.getLength(); i++) {
Person person = new Person();
//得到第一个person节点
Element personNode = (Element) items.item(i);
//获取person节点的id属性值
person.setId(new Integer(personNode.getAttribute("id")));
//获取person节点下的所有子节点(标签之间的空白节点和name/age元素)
NodeList childsNodes = personNode.getChildNodews();
for( int j =0; j < childsNodes.getLength(); j++) {
Node node = (Node) childsNodes.item(j);
//判断是否为元素类型
if( node.getNodeType() == Node.ELEMENT_NODE) {
Element childNode = (Element) node;
if( "name".equals(childNode.getNodeName())) {
//获取name元素下Text节点,然后从Text节点获取数据
person.setName( childNode.getFirstChild().getNodeValue());
} else if ("age".equals(childNode.getNodeName())) {
person.setAge( new Short(childNode.getFirstChild().getNodeValue()));
}
}
}
persons.add(person);
}
inStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return persons;
}
- SAX解析器:
SAX解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档结束,不可暂停或倒退。
它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,
一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器
才能根据提供的事件信息来决定自己的行为。
SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。
SAX的工作原理: 对文档进行顺序扫描,当扫描到文档(document)开始与结束,元素(element)开始与结束,文档(document)结束等
地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描直到文档结束。
在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。
事件处理器是org.xml.sax包中ContentHandler, DTDHandler, ErrorHandler,以及EntityResolver这4个接口。
XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHandler, DTDHandler, ErrorHandler,
以及EntivityResolver这4个接口的连接。
常用的SAX接口和类:
Attributes: 用于得到属性的个数,名字和值。
ContentHandler: 定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。
DTDHandler: 定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。
DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。
EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。
ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。
DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。
示例:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>李明</name>
<age>30</age>
</person>
<person id="20">
<name>李向梅</name>
<age>25</age>
</person>
</persons>
public class Person implements Serializable{
private Integer id;
private String name;
private Short age;
public Person() {
}
public Person(String name, Short age) {
super();
this.name = name;
this.age = 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 Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
}
public class XMLContenteHandler extends DefaultHandler {
<span style="color:#000000;"> //临时变量,解决sax读取XML时不能读取换行符号后面的内容的问题;注意temp=""不能写成temp或temp=null,避免脏数据</span>
private String temp="";
private String preTag;
private Person person;
private List<Person> persons;
public void startDocument() throws SAXException {
persons = new ArrayList<Person>();
}
public void characters(char[] ch, int start, int length) throws SAXException {
if(person != null) {
String date = new String(ch, start, length);
if("name".equals(preTag)){
temp += date;
} else if("age".equals(preTag)){
temp += date;
}
}
}
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
if("person".equals(localName)){
person = new Person();
person.setId(attributes.getIndex("id"));
}
preTag = localName;
}
public void endElement( String uri, String localName, String name) throws SAXException {
if(person != null && "name".equals(localName)) {
person.setName(temp);
temp="";
} else if(person != null && "age".equals(localName)){
person.setAge(new Short(temp));
temp="";
} else if(person != null && "person".equals(localName)) {
persons.add(person);
person = null;
}
preTag =null;
}
public List<person> getPersons() {
return persons;
}
}
public class SaxPersonService {
// 业务层要对外抛出
public static List<Person> readXML(InputStrean inStrean) throws Exception {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();//创建解析器
//设置解析器的相关特性,http://xnl.org/sax/features/namespaces = true;
//表示开启命名空间特性
//saxParser.setProperty("http://xnl.org/sax/features/namespaces", true);
XMLContentHandler handler = new XMLContentHandler();
saxParser.parse(inStream, handler);
inStream.close();
return handler.getPersons();
}
}
public class SaxPersonServiceTest extends AndroidTestCase{
private final String TAG = "SaxPersonServiceTest";
public void testReadXML() throws Exception{
//ljq.xml放在src目录下
InputStream inputStream = SaxPersonServiceTest.class.getClassLoader().getResourceAsStream("ljq.xml");
List<Person> persons = SaxPersonService.readXML(inputStream);
for(Person person : persons){
Log.i(TAG, person.getId() + " : " + person.getName() + " : " + person.getAge());
}
}
}
- PULL解析器
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。
PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。
PULL 的工作原理:XML pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。
读取XML
XmlPullParser pullParser = Xml.newPullParser();
pullParser.setInput(xml, "UTF-8"); // 为破解器添加要解析的xml数据
int event = pullParser.getEventType(); //开始读取,获取事件返回值
pullParser.getName(); // 获取节点名称
pullParser.getAttributeValue(0); // 获取第一个属性的值
pullParser.nextText(); //获取标签之后的节点内容
event = pullParser.next(); // 解析器遇到结束标签不会自动向下解析,需要调用此方法进行继续执行
保存文件到XML
public static void save(List<Person> persons, OutputStream outStream)
throws Exception, IllegalStateException, IOException {
XmlSerializer serializer = Xml.newSerializer();//获取XML写入信息的序列化对象
serializer.setOutput(outStream, "UTF-8");//设置要写入的OutputStream
serializer.startDocument("UTF-8", true);//设置文档标签
serializer.startTag(null, "persons");//设置开始标签,第一个参数为namespace
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();
outStream.flush();
outStream.close();
}