在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元素名字和属性的方法。
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,
以及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 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(); }