简介
在Android中,XML资源存储在assets或者res\xml或者res\raw目录中,我们在Eclipse构建Android项目是,xml及raw目录初始是未创建的,如果我们需要用到原始的xml资源,需要我们自己手动构建文件夹,并存储我们的资源到指定的(这里是raw或者xml)目录中。有了xml资源,我们在应用中就需要解析它,在Android中,常见的解析xml的方式有一下三种:SAX、Pull、Dom解析方式。
Pull方式解析
Pull解析采用的是事件驱动的方式来解析XML文档,当我们开始解析后,我们可以通过调用XmlResourceParser.next()解析下一个解析事件(开始文档,结束文档,开始标签,结束标签)。当我们解析到某一个元素是我们可以通过XmlPullParser.getAttributte()方法来获取属性的值,也可调用它的XmlPullParser.nextText()获取本节点的值.
接下来我们会用一个XML文件并把它解析到一个对象集合中,首先我们需要构建我们要演示的XML文件以及我们的对象类
如下所示,我们用如下的XML文件来演示三种解析方式
<?xml version="1.0" encoding="utf-8"?> <resources> <customer name="tom" age="20" gender="male" > <telphone> <phone>13636713128</phone> </telphone> <email>tom@yahoo.com</email> </customer> <customer name="kite" age="21" gender="male" > <telphone/> <email>kite@qq.com</email> </customer> <customer name="jeriffe" age="28" gender="male" > <telphone> <phone>13636713128</phone> <phone>13892008888</phone> </telphone> <email>jeriffe@126.com</email> </customer> </resources>
XML文件我们分别存储在如下所示的3个目录中:
如下所示,是我们将要用到的对象类:
package com.amaker.ch03.xml; import java.util.ArrayList; import java.util.List; public class Person { private String name; private String sex; private int age; private String email; private List<String> phonesList; public Person() { phonesList = new ArrayList<String>(); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List<String> getPhonesList() { return phonesList; } public void setPhonesList(List<String> phonesList) { this.phonesList = phonesList; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void addPhone(String phoneNumber) { this.phonesList.add(phoneNumber); } @Override public String toString() { String phoneString = ""; for (String phone : phonesList) { phoneString += "\n " + phone; } return String.format("姓名:%s\n年龄:%d\n性别:%s\nEmail:%s\n电话:%s", name, age,sex, email, phoneString); } }
下面的代码片段是一个用Pull方式解析的示例(文件存储在res\xml目录下)
private List<Person> parseByXmlPullParser() { List<Person> resultsList = new ArrayList<Person>(); Person person = null; Resources resources = getResources(); XmlResourceParser xrParser = resources.getXml(R.xml.customers); try { while (xrParser.getEventType() != XmlResourceParser.END_DOCUMENT) { if (xrParser.getEventType() == XmlResourceParser.START_TAG) { String name = xrParser.getName(); if (name.equals("customer")) { person = new Person(); person.setName(xrParser.getAttributeValue(null, "name")); person.setAge(Integer.parseInt(xrParser .getAttributeValue(1))); person.setSex(xrParser.getAttributeValue(2)); } else if (name.equals("phone")) { person.addPhone(xrParser.nextText()); } else if (name.equals("email")) { xrParser.next(); person.setEmail(xrParser.getText()); } } else if (xrParser.getEventType() == XmlPullParser.END_TAG) { String name = xrParser.getName(); if (name.equals("customer")) { resultsList.add(person); } } xrParser.next(); } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return resultsList; }
SAX解析
SAX即是:Simple API for XML。SAX解析也是一种基于事件驱动的API,有两个部分,解析器和事件处理器,解析器就是XMLReader接口,负责读取XML文档和向事件处理器发送事件(也是事件源),事件处理器接口,负责对发送的事件响应和进行XML文档处理。
事件处理器有4个接口:
1.EntityResolver
2.DTDHandler
3.ContentHandler
4.ErrorHandler
解析器通过其setXXX()方法来注册事件处理器,在我们自己的解析中,我们不需要实现上述的事件处理器接口,因为SAX已经为我们提供了一个默认的基类DefaultHandler,我们只需要extend这个接口来实现我们的解析逻辑既可。
用SAX解析需要如下的几个步骤:
1:构建SAXParserFactory对象
2: 构建SAXParser解析器:根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器
3:构建XMLReader:根据SAXParser解析器获取事件源对象XMLReader
4:构建一个自定义的Handler对象
5:连接事件源对象XMLReader到事件处理类DefaultHandler中
6:读取文件流
7:调用XMLReader的parse方法从输入源中获取到的xml数据
8:通过我们自定义的Handler返回我们需要的数据集合。
下面我们来看一个SAX解析的样例
注:SAX解析的XML文件要存储在res\raw目录中,存储在res\xml目录中运行时报错。
// Simple API for XML private List<Person> parseBySaxParser() throws ParserConfigurationException, SAXException, IOException { // 1.构建一个工厂SAXParserFactory SAXParserFactory parserFactory = SAXParserFactory.newInstance(); // 2.构建并实例化SAXPraser对象 SAXParser saxParser = parserFactory.newSAXParser(); // 3.构建XMLReader解析器 XMLReader xmlReader = saxParser.getXMLReader(); // 4.这里是我们的具体类型的Handler对象 PersonSaxParserHandler parserHandler = new PersonSaxParserHandler(); // 5.解析器注册一个处理器 xmlReader.setContentHandler(parserHandler); // 6.读取文件流 InputStream stream = getResources().openRawResource(R.raw.customers); InputSource is = new InputSource(stream); // 6.解析文件 xmlReader.parse(is); List<Person> resList = parserHandler.getPersons(); return resList; }
我们继承扩展的事件处理器
package com.amaker.ch03.xml; import java.util.ArrayList; import java.util.List; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import android.R.integer; public class PersonSaxParserHandler extends DefaultHandler { class PersonConst { public static final int Persons = 1; public static final int Person_TELPHONE = 2; public static final int Person_PHONE = 3; public static final int Person_EMAIL = 4; } private List<Person> persons = new ArrayList<Person>(); private Person person = null; private int currentState = 0; @Override public void characters(char[] ch, int start, int length) throws SAXException { String theString = new String(ch, start, length); switch (currentState) { case PersonConst.Person_EMAIL : person.setEmail(theString); break; case PersonConst.Person_PHONE : person.addPhone(theString); break; default : break; } currentState=0; } @Override public void startDocument() throws SAXException { // 文档解析开始 super.startDocument(); } @Override public void endDocument() throws SAXException { // 文档解析开始 } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { String node = localName.length() != 0 ? localName : qName; // 开始解析节点 if (node.equals("customer")) { currentState = PersonConst.Persons; person = new Person(); person.setName(attributes.getValue("name")); person.setAge(Integer.parseInt(attributes.getValue("age"))); person.setSex(attributes.getValue("gender")); return; } if (node.equals("phone")) { currentState = PersonConst.Person_PHONE; return; } if (node.equals("email")) { currentState = PersonConst.Person_EMAIL; return; } currentState=0; } @Override public void endElement(String uri, String localName, String qName) throws SAXException { // 解析节点结束 if (localName.equals("customer")) { persons.add(person); return; } } public List<Person> getPersons() { return persons; } }
DOM解析
DOM方式解析xml是先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据的,但是这样一来,如果xml文件很大呢?手机CPU处理能力当然不能与PC机器比,因此在处理性能上会有折损。所以如果XML文件比较大,建议还是用上述的方式,而不用DOM方式。
DOM解析的步骤一般如下:
1.构建一个DocumentBuilderFactory实例
2.构建DocumentBuilder
3.加载XML文档(Document)
4.遍历XML文档
下来我们来看DOM解析的一个示例(文件存储在assets目录下)
private List<Person> parseByDomParser() throws ParserConfigurationException, SAXException, IOException { List<Person> resList = new ArrayList<Person>(); // 构建一个DocumentBuilderFactory实例 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 构建DocumentBuilder DocumentBuilder builder = factory.newDocumentBuilder(); // 读取文件流 AssetManager assetManager=getAssets(); InputStream stream=assetManager.open("customers.xml"); //InputStream stream = getResources().openRawResource(R.raw.customers); // 得到代表整个xml的Document对象 Document document = builder.parse(stream); // 遍历所有节点 Element root = document.getDocumentElement(); // 获取根节点的所有customer的节点 NodeList nodes = root.getElementsByTagName("customer"); for (int index = 0; index < nodes.getLength(); index++) { Person person = new Person(); // 获取person元素节点 Element personElement = (Element) (nodes.item(index)); // 获取person中属性(Attributes)值 person.setName(personElement.getAttribute("name")); person.setAge(Integer.parseInt(personElement.getAttribute("age"))); person.setSex(personElement.getAttribute("gender")); // 获取telphone下标签 Element telphoneElement = (Element) personElement .getElementsByTagName("telphone").item(0); NodeList phoneNodes = telphoneElement.getElementsByTagName("phone"); for (int pIndex = 0; pIndex < phoneNodes.getLength(); pIndex++) { // 获取phone元素节点 Element phoneElement = (Element) (phoneNodes.item(pIndex)); person.addPhone(phoneElement.getFirstChild().getNodeValue()); } // 获取email下标签 Element emailElement = (Element) personElement .getElementsByTagName("email").item(0); person.setEmail(emailElement.getFirstChild().getNodeValue()); resList.add(person); } return resList; }
下面就是我们的Activity类及运行效果图
public class TestXmlActivity extends Activity implements View.OnClickListener { private TextView myTextView; private Button pullButton, saxButton, domButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_xml); setupViews(); } private void setupViews() { myTextView = (TextView) findViewById(R.id.xmlContentTextView01); pullButton = (Button) findViewById(R.id.xmltTestButton01); pullButton.setOnClickListener(this); saxButton = (Button) findViewById(R.id.xmltTestButton02); saxButton.setOnClickListener(this); domButton = (Button) findViewById(R.id.xmltTestButton03); domButton.setOnClickListener(this); } public void onClick(View v) { StringBuilder sb = new StringBuilder(); List<Person> resultsList = new ArrayList<Person>(); try { switch (v.getId()) { case R.id.xmltTestButton01 : resultsList = parseByXmlPullParser(); sb.append("使用Pull方式解析XML\n"); break; case R.id.xmltTestButton02 : resultsList = parseBySaxParser(); sb.append("使用SAX方式解析XML\n"); break; case R.id.xmltTestButton03 : resultsList = parseByDomParser(); sb.append("使用DOM方式解析XML\n"); break; default : break; } } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (Person person : resultsList) { sb.append("*************************************************\n"); sb.append(person.toString()); sb.append("\n"); } myTextView.setText(sb.toString()); } }
Activity的Layout文件比较简单,3个Button及一个TextView控件,这里就不贴其代码了。
结束语
以上我们介绍了在Android下常用的3种解析XML文件的方式,我们也分别演示了从res\xml、res\raw、及assets目录下获取XML文件,如果想深入了解这3中方式,可以查看相关的SDK文档。