一、SAX解析技术

Sax使用的是事件驱动的流式解析技术。事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。当解析到文档的开始或结束、元素的开始或结束等都会触发一个事件,我们在事件处理方法中完成对数据的操作。由此可见,我们需要编写实现了事件接口的类。

1.创建Android工程(eclipse3.5): 


 Project name:AndroidXML 


 BuildTarget:Android2.1 


 Application name:Android XML 解析技术 


 Package name:com.changcheng.androidxml 


 Create Activity:AndroidXML 


 Min SDK Version:7 




 2.需要解析的XML文件: 


<?xml version="1.0" encoding="UTF-8"?> 


<books> 


 <book id="23"> 


 <name>C++ Primer 4</name> 


 <price>78</price> 


 </book> 


 <book id="20"> 


 <name>Think in Java</name> 


 <price>76</price> 


 </book> 


</books> 


 该文件存放于src源码目录。 




 3.XML文件对应的实体Book: 


package com.changcheng.androidxml.entity; 




public class Book { 


 private int id; 


 private String name; 


 private float price; 




 public Book() { 


 } 




 public Book(int id, String name, float price) { 


 this.id = id; 


 this.name = name; 


 this.price = price; 


 } 




 public int getId() { 


 return id; 


 } 




 public void setId(int id) { 


 this.id = id; 


 } 




 public String getName() { 


 return name; 


 } 




 public void setName(String name) { 


 this.name = name; 


 } 




 public float getPrice() { 


 return price; 


 } 




 public void setPrice(float price) { 


 this.price = price; 


 } 




 @Override 


 public String toString() { 


 return "Book [name=" + name + ", price=" + price + "]"; 


 } 


} 




 4.Sax解析XML的事件处理类: 


 Sax的事件处理类必须实现ContentHandler接口,但我们在这个例子中不需要使用到ContentHandler接口的所有方法,我们仅需要其中的3个方法。所以Sax为我们提供了一个没有进行任何操作的ContentHandler实现类DefaultHandler。我们直接继承DefaultHandler类,并重写我们需要的方法即可。 


package com.changcheng.androidxml.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 com.changcheng.androidxml.entity.Book; 




public class SaxXmlContentHandler extends DefaultHandler { 




 private List<Book> books; 


 private Book book; 


 private String tagName; 




 public List<Book> getBooks() { 


 return books; 


 } 




 /** 


 * 接收文档的开始的通知。 


 */ 


 @Override 


 public void startDocument() throws SAXException { 


 this.books = new ArrayList<Book>(); 


 } 




 /** 


 * 接收字符数据的通知。 


 */ 


 @Override 


 public void characters(char[] ch, int start, int length) 


 throws SAXException { 


 if (this.tagName != null) { 


 String data = new String(ch, start, length); 


 if (this.tagName.equals("name")) { 


 this.book.setName(data); 


 } else if (this.tagName.equals("price")) { 


 this.book.setPrice(Float.parseFloat(data)); 


 } 


 } 


 } 




 /** 


 * 接收元素开始的通知。 


 * namespaceURI:元素的命名空间 


 * localName:元素的本地名称(不带前缀) 


 * qName:元素的限定名(带前缀) 


 * atts:元素的属性集合 


 */ 


 @Override 


 public void startElement(String uri, String localName, String qName, 


 Attributes attributes) throws SAXException { 


 if (localName.equals("book")) { 


 book = new Book(); 


 book.setId(Integer.parseInt(attributes.getValue(0))); 


 } 


 this.tagName = localName; 


 } 




 /** 


 * 接收文档的结尾的通知。 


 * uri:元素的命名空间 


 * localName:元素的本地名称(不带前缀) 


 * name:元素的限定名(带前缀) 


 */ 


 @Override 


 public void endElement(String uri, String localName, String qName) 


 throws SAXException { 


 if (localName.equals("book")) { 


 this.books.add(this.book); 


 } 


 this.tagName = null; 


 } 


}





5.编写测试Sax解析XML的类

在创建工程时,生成的AndroidXML.java,并没有被使用到。因为我们使用Android的单元测试,运行上面的程序。



编写Android单元测试类:

package com.changcheng.androidxml.test; 




import java.io.InputStream; 


import java.io.StringWriter; 


import java.util.ArrayList; 


import java.util.List; 


import com.changcheng.androidxml.entity.Book; 


import com.changcheng.androidxml.xml.AndoridSaxXml; 


import com.changcheng.androidxml.xml.AndroidPullXML; 


import android.test.AndroidTestCase; 


import android.util.Log; 




public class TestAndroidXML extends AndroidTestCase { 




 private static final String TAG = "TestAndroidXML"; 




 /** 


 * 测试Sax解析XML 


 * @throws Throwable 


 */ 


 public void testAndroidSaxReadXML() throws Throwable{ 


 InputStream file = this.getClass().getClassLoader().getResourceAsStream("books.xml"); 


 try { 


 List<Book> books = AndoridSaxXml.readXML(file); 


 Log.i(TAG, books.toString()); 


 } catch (Exception e) { 


 Log.e(TAG, e.toString()); 


 } 


 } 


} 


 测试类必须继承自AndroidTestCase类,Android的单元测试使用的是JUnit3,所以在我们的测试方法名称要以test开头。 




 再编写一个AndoridSaxXml(测试类中使用到的)类: 


package com.changcheng.androidxml.xml; 




import java.io.InputStream; 


import java.util.List; 


import javax.xml.parsers.SAXParser; 


import javax.xml.parsers.SAXParserFactory; 


import com.changcheng.androidxml.entity.Book; 




public class AndoridSaxXml { 




 public static List<Book> readXML(InputStream inputStream) throws Exception { 


 // 创建Sax解析 


 SAXParserFactory saxParFac = SAXParserFactory.newInstance(); 


 SAXParser saxParser = saxParFac.newSAXParser(); 


 SaxXmlContentHandler handler = new SaxXmlContentHandler(); 


 // 解析XML文件 


 saxParser.parse(inputStream, handler); 


 inputStream.close(); 


 return handler.getBooks(); 


 } 


}




6.运行测试

在outline面板中的testAndroidSaxReadXML方法或在TestAndroidXML类的testAndroidSaxReadXML方法上右键->Debug As->Android Junit Test。运行结束后在LogCat面板中查看运行结束。



关于使用Sax生成XML文档,我在此就不做总结了。下面的Pull技术才是我们进行Android开发的重点。



二、Pull解析技术

Pull解析技术与Sax解析技术原理相同,但比Sax解析简单,它们的解析速度和占用的资源差不多。Android内部使用的XML解析技术正是Pull,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。下面我们使用Pull解析技术解析XML文件,然后再使用Pull技术生成XML文件。



Pull解析XML文档

1.XML文件

依然使用上面的books.xml



2.XML文档对应的实体Book

依然使用上面的Book.java



3.Pull解析XML类

package com.changcheng.androidxml.xml; 




import java.io.InputStream; 


import java.io.Writer; 


import java.util.ArrayList; 


import java.util.List; 


import org.xmlpull.v1.XmlPullParser; 


import org.xmlpull.v1.XmlPullParserFactory; 


import org.xmlpull.v1.XmlSerializer; 


import android.util.Xml; 


import com.changcheng.androidxml.entity.Book; 




public class AndroidPullXML { 




 public static List<Book> readXML(InputStream inputStream, 


 String inputEncoding) throws Exception { 


 // 创建Pull解析 


 XmlPullParserFactory pullParserFactory = XmlPullParserFactory 


 .newInstance(); 


 XmlPullParser pullParser = pullParserFactory.newPullParser(); 


 // 解析XML 


 pullParser.setInput(inputStream, inputEncoding); 


 // 开始 


 int eventType = pullParser.getEventType(); 




 List<Book> books = null; 


 Book book = null; 


 while (eventType != XmlPullParser.END_DOCUMENT) { 


 String nodeName = pullParser.getName(); 


 switch (eventType) { 


 // 文档开始 


 case XmlPullParser.START_DOCUMENT: 


 books = new ArrayList<Book>(); 


 break; 


 // 节点开始 


 case XmlPullParser.START_TAG: 


 if ("book".equals(nodeName)) { 


 book = new Book(); 


 book.setId(Integer 


 .parseInt(pullParser.getAttributeValue(0))); 


 } else if ("name".equals(nodeName)) { 


 book.setName(pullParser.nextText()); 


 } else if ("price".equals(nodeName)) { 


 book.setPrice(Float.parseFloat(pullParser.nextText())); 


 } 


 break; 


 // 节点结束 


 case XmlPullParser.END_TAG: 


 if ("book".equals(nodeName)) { 


 books.add(book); 


 book = null; 


 } 


 break; 


 } 


 eventType = pullParser.next(); 


 } 


 return books; 


 } 


}





4.编写测试Pull解析XML类

在sax测试类TestAndroidXML中添加一个测试方法: 


/** 


 * 测试Pull解析XML 


 * @throws Throwable 


 */ 


public void testAndroidPullReadXML() throws Throwable { 


 InputStream file = this.getClass().getClassLoader().getResourceAsStream("books.xml"); 


 try { 


 List<Book> books = AndroidPullXML.readXML(file, "UTF-8"); 


 Log.i(TAG, books.toString()); 


 } catch (Exception e) { 


 Log.e(TAG, e.toString()); 


 } 


}




5.运行测试

在outline面板中的testAndroidPullReadXML方法或在TestAndroidXML类的testAndroidPullReadXML方法上右键->Debug As->Android Junit Test。运行结束后在LogCat面板中查看运行结束。



Pull生成XML文档

使用Pull生成上面的books.xml文档。



1.在AndroidPullXML类中添加一个方法:

public static void writeXML(Writer writer, List<Book> books) 


 throws Exception { 


 // 创建XML生成器 


 XmlSerializer writexml = Xml.newSerializer(); 


 writexml.setOutput(writer); 


 // 生成XML文档 


 writexml.startDocument("UTF-8", true); 


 writexml.startTag("", "books"); 


 for (Book book : books) { 


 // name 


 writexml.startTag("", "name"); 


 writexml.attribute("", "id", book.getId() + ""); 


 writexml.text(book.getName()); 


 writexml.endTag("", "name"); 


 // price 


 writexml.startTag("", "price"); 


 writexml.text(book.getPrice() + ""); 


 writexml.endTag("", "price"); 


 } 


 // 


 writexml.endTag("", "books"); 


} 




 2.编写测试Pull生成XML方法 


 在sax测试类TestAndroidXML中添加一个测试方法: 


/** 


 * 测试Pull生成XML 


 * @throws Throwable 


 */ 


public void testAndroidPullWriteXML() throws Throwable { 


 // 生成到内存中。(也可以生成到文件中,那就需要定义一个文件输出流。) 


 StringWriter writer = new StringWriter(); 


 // 添加三本书 


 List<Book> books = new ArrayList<Book>(); 


 books.add(new Book(1, "C", 89)); 


 books.add(new Book(1, "C++", 100)); 


 books.add(new Book(1, "Java", 87)); 


 books.add(new Book(1, "JavaEE", 95)); 


 // 生成XML 


 AndroidPullXML.writeXML(writer, books); 


 // 打印结果 


 Log.i(TAG, books.toString()); 




}




3.运行测试

在outline面板中的testAndroidPullWriteXML方法或在TestAndroidXML类的testAndroidPullWriteXML方法上右键->Debug As->Android Junit Test。运行结束后在LogCat面板中查看运行结束。



OK,使用Sax和Pull在Android中解析XML文档到此完成。在Andorid中还可以使用DOM技术,使用DOM技术解析在我们学习JavaWeb基础时,已经做了总结,在此就不再介绍了。