XML常用的三种解析
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.xmlanalysis.MainActivity" >
<Button
android:id="@+id/main_dom_analysis"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DOM解析" />
<Button
android:id="@+id/main_dom_back_analysis "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DOM反解析" />
<Button
android:id="@+id/main_sax_analysis"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SAX解析" />
<Button
android:id="@+id/main_sax_back_analysis "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SAX反解析" />
<Button
android:id="@+id/main_pull_analysis"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PULL解析" />
<Button
android:id="@+id/main_pull_back_analysis "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PULL反解析" />
</LinearLayout>
在res文件夹创建一个asset文件夹,在asset文件夹创建一个book.xml
<?xml version="1.0" encoding="utf-8"?>
<books>
<book>
<id>1</id>
<name>红楼梦</name>
<price>34</price>
</book>
<book>
<id>2</id>
<name>三国演义</name>
<price>23</price>
</book>
<book>
<id>3</id>
<name>水浒传</name>
<price>54</price>
</book>
<book>
<id>4</id>
<name>西游记</name>
<price>83</price>
</book>
</books>
package com.example.xmlanalysis.entity;
public class Book {
private int bookId;
private String bookName;
private double bookPrice;
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public double getBookPrice() {
return bookPrice;
}
public void setBookPrice(double bookPrice) {
this.bookPrice = bookPrice;
}
public Book(int bookId, String bookName, double bookPrice) {
super();
this.bookId = bookId;
this.bookName = bookName;
this.bookPrice = bookPrice;
}
public Book() {
super();
}
@Override
public String toString() {
return "Book [bookId=" + bookId + ", bookName=" + bookName + ", bookPrice=" + bookPrice + "]";
}
}
package com.example.xmlanalysis.util;
import java.io.InputStream;
import java.util.List;
import com.example.xmlanalysis.entity.Book;
public interface BookXml {
/**
* 获取输入流对象,将其转换成book集合
* @param is 输入流对象
* @return book集合
* @throws Exception
*/
public List<Book> getBookList(InputStream is)throws Exception;
/**
* 获取book集合对象,将其转换成String流
* @param bookList book集合
* @return String流
* @throws Exception
*/
public String setBookList(List<Book> bookList)throws Exception;
}
1、DOM解析
DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。
整个文档是一个节点文档,每一个XML标签是一个元素节点,包含在XML元素中的文本是文本节点,每一个XML属性是一个属性节点
package com.example.xmlanalysis.util;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
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.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.example.xmlanalysis.entity.Book;
public class DOMUtil implements BookXml {
/**
* DOM实际上就是document解析,通过文档工厂获取文档对象,并对文档对象的各个节点进行访问
*/
@Override
public List<Book> getBookList(InputStream is) throws Exception {
List<Book> bookList = new ArrayList<Book>();
// 创建文档加载工厂,相当于单例的方法
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 实例化文档加载对象
DocumentBuilder db = dbf.newDocumentBuilder();
// 解析输入流获取文档对象
Document d = db.parse(is);
// 获取文档元素对象
Element element = d.getDocumentElement();
// 获取名称为book的所有节点集合
NodeList nl = element.getElementsByTagName("book");
for (int i = 0; i < nl.getLength(); i++) {
Book book = new Book();
Node bookNode = nl.item(i);
NodeList bookEntity = bookNode.getChildNodes();
for (int j = 0; j < bookEntity.getLength(); j++) {
Node bookEntityNode = bookEntity.item(j);
String nodeName = bookEntityNode.getNodeName();
if (nodeName.equals("id")) {
book.setBookId(Integer.valueOf(bookEntityNode
.getFirstChild().getNodeValue()));
} else if (nodeName.equals("name")) {
book.setBookName(bookEntityNode.getFirstChild()
.getNodeValue());
} else if (nodeName.equals("price")) {
book.setBookPrice(Double.valueOf(bookEntityNode
.getFirstChild().getNodeValue()));
}
}
bookList.add(book);
}
return bookList;
}
@Override
public String setBookList(List<Book> bookList) throws Exception {
// 创建文档加载工厂,相当于单例的方法
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 实例化文档加载对象
DocumentBuilder db = dbf.newDocumentBuilder();
//获取单例的document文档对象
Document d=db.newDocument();
//创建xml总结点books,并返回该节点对象
Element element=d.createElement("books");
for (Book book:bookList) {
Element bookElement=d.createElement("book");
//采用setAttribute方式会将该节点作为头结点的内部节点
bookElement.setAttribute("id", book.getBookId()+"");
//通过setTextContent方式将该节点放入book节点
Element nameElement=d.createElement("name");
nameElement.setTextContent(book.getBookName());
bookElement.appendChild(nameElement);
//通过setTextContent方式将该节点放入book节点
Element priceElement=d.createElement("name");
priceElement.setTextContent(book.getBookPrice()+"");
bookElement.appendChild(priceElement);
//将构造好的book节点作为外部节点books的子节点
element.appendChild(bookElement);
}
//将books节点作为整个文档的总结点
d.appendChild(element);
//通过单例模式构造TransformerFactory
TransformerFactory tf=TransformerFactory.newInstance();
Transformer t=tf.newTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
StringWriter writer=new StringWriter();
//表明文档来源是document
Source source=new DOMSource(d);
Result result=new StreamResult(writer);
// 开始转换
t.transform(source, result);
return writer.toString();
}
}
2、SAX解析
SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。
package com.example.xmlanalysis.util;
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.example.xmlanalysis.entity.Book;
/**
* 类似于适配器,将装配进来的inputstream转换成我们需要的格式
* @author Administrator
*
*/
public class MyHandler extends DefaultHandler{
private List<Book> bookList;
private Book book;
private StringBuilder builder;
/**
* 调用该方法将解析后的数据结构返回到外部调用
* @return
*/
public List<Book> getBookList(){
return bookList;
}
/**
* 当文件开始扫描的时候,初始化存储集合和字符串缓冲流
*/
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
bookList=new ArrayList<Book>();
builder=new StringBuilder();
}
/**
* 开始扫描文档内部的每个元素
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
super.startElement(uri, localName, qName, attributes);
if (localName.equals("book")) {
book=new Book();
}
//设置字符长度为0,以便重新开始读取元素的字符节点
builder.setLength(0);
}
/**
* 将读取的xml内容追加到builder字符串中
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
super.characters(ch, start, length);
builder.append(ch, start, length);
}
/**
* 扫描接收后,将字符串内容转换成对应的对象
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
super.endElement(uri, localName, qName);
if (localName.equals("id")) {
book.setBookId(Integer.valueOf(builder.toString()));
}else if (localName.equals("name")) {
book.setBookName(builder.toString());
}else if (localName.equals("price")) {
book.setBookPrice(Double.valueOf(builder.toString()));
}else if (localName.equals("book")) {
bookList.add(book);
}
}
}
package com.example.xmlanalysis.util;
import java.io.InputStream;
import java.io.StringWriter;
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.helpers.AttributesImpl;
import com.example.xmlanalysis.entity.Book;
public class SAXUtil implements BookXml{
@Override
public List<Book> getBookList(InputStream is) throws Exception {
//SAX解析工厂类
SAXParserFactory spf=SAXParserFactory.newInstance();
//SAX解析对象
SAXParser sp=spf.newSAXParser();
//构造自定义handler
MyHandler my=new MyHandler();
sp.parse(is, my);
return my.getBookList();
}
@Override
public String setBookList(List<Book> bookList) throws Exception {
//通过单例模式构造SAXTransformerFactory
SAXTransformerFactory stf=(SAXTransformerFactory) TransformerFactory.newInstance();
//构造SAX构造器
TransformerHandler th=stf.newTransformerHandler();
//获取文档格式对象
Transformer t=th.getTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "utf-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
StringWriter sw=new StringWriter();
Result result=new StreamResult(sw);
th.setResult(result);
//uri代表我们的命名空间
String uri="";
//设置命名空间的本地名称
String localName="";
th.startDocument();
th.startElement(uri, localName, "books", null);
//负责存放元素属性
AttributesImpl ai=new AttributesImpl();
char[] ch=null;
for (Book book : bookList) {
//每扫描一次对象,就要清空来源属性
ai.clear();
ai.addAttribute(uri, localName, "id", "string", String.valueOf(book.getBookId()));
th.startElement(uri, localName, "book", ai);
//将book中的name字段的值转换成字符数组
th.startElement(uri, localName, "name", null);
ch=String.valueOf(book.getBookName()).toCharArray();
th.characters(ch, 0, ch.length);
th.endElement(uri, localName, "name");
th.startElement(uri, localName, "price", null);
ch=String.valueOf(book.getBookName()).toCharArray();
th.characters(ch, 0, ch.length);
th.endElement(uri, localName, "price");
th.endElement(uri, localName, "book");
}
th.endElement(uri, localName, "books");
th.endDocument();
return sw.toString();
}
}
3、PULL解析
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器。
package com.example.xmlanalysis.util;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import android.util.Xml;
import com.example.xmlanalysis.entity.Book;
public class PULLUtil implements BookXml{
@Override
public List<Book> getBookList(InputStream is) throws Exception {
List<Book> bookList=null;
Book book=null;
//创建PULL解析器
XmlPullParser xpp=Xml.newPullParser();
//指明解析器的输入流并指明编码方式
xpp.setInput(is, "utf-8");
int eventType=xpp.getEventType();
//当整个文档扫描还没结束时,不断执行相应的操作
while (eventType!=XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
bookList=new ArrayList<Book>();
break;
case XmlPullParser.START_TAG:
if (xpp.getName().equals("book")) {
book=new Book();
}else if (xpp.getName().equals("id")) {
eventType=xpp.next();
book.setBookId(Integer.valueOf(xpp.getText()));
}else if (xpp.getName().equals("name")) {
eventType=xpp.next();
book.setBookName(xpp.getText());
}else if (xpp.getName().equals("price")) {
eventType=xpp.next();
book.setBookPrice(Double.valueOf(xpp.getText()));
}
break;
case XmlPullParser.END_TAG:
if (xpp.getName().equals("book")) {
bookList.add(book);
book=null;
}
break;
default:
break;
}
eventType=xpp.next();
}
return bookList;
}
@Override
public String setBookList(List<Book> bookList) throws Exception {
XmlSerializer xs=Xml.newSerializer();
StringWriter sw=new StringWriter();
//设置结果的输出对象是writer
xs.setOutput(sw);
xs.startDocument("utf-8", true);
xs.startTag("", "books");
for (Book book : bookList) {
xs.startTag("", "book");
xs.attribute("", "id", book.getBookId()+"");
xs.startTag("", "name");
xs.text(book.getBookName());
xs.endTag("", "name");
xs.startTag("", "price");
xs.text(book.getBookPrice()+"");
xs.endTag("", "price");
xs.endTag("", "book");
}
xs.endTag("", "books");
xs.endDocument();
return sw.toString();
}
}
package com.example.xmlanalysis;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import com.example.xmlanalysis.entity.Book;
import com.example.xmlanalysis.util.DOMUtil;
import com.example.xmlanalysis.util.PULLUtil;
import com.example.xmlanalysis.util.SAXUtil;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener{
private Button dom1,dom2,sax1,sax2,pull1,pull2;
private InputStream is;
private List<Book> bookList;
private String str;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dom1=(Button) findViewById(R.id.main_dom_analysis);
dom2=(Button) findViewById(R.id.main_dom_back_analysis);
sax1=(Button) findViewById(R.id.main_sax_analysis);
sax2=(Button) findViewById(R.id.main_sax_back_analysis);
pull1=(Button) findViewById(R.id.main_pull_analysis);
pull2=(Button) findViewById(R.id.main_pull_back_analysis);
dom1.setOnClickListener(this);
dom2.setOnClickListener(this);
sax1.setOnClickListener(this);
sax2.setOnClickListener(this);
pull1.setOnClickListener(this);
pull2.setOnClickListener(this);
try {
is=this.getAssets().open("book.xml");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
DOMUtil domUtil=new DOMUtil();
SAXUtil saxUtil=new SAXUtil();
PULLUtil pullUtil=new PULLUtil();
switch (v.getId()) {
case R.id.main_dom_analysis:
try {
bookList=domUtil.getBookList(is);
String content="";
for (Book b : bookList) {
content+=b.toString();
}
Toast.makeText(MainActivity.this, "dom解析:"+content, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.main_dom_back_analysis:
try {
String str=domUtil.setBookList(bookList);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.main_sax_analysis:
try {
bookList=saxUtil.getBookList(is);
String content="";
for (Book book : bookList) {
content+=book.toString();
}
Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.main_sax_back_analysis:
try {
String str = saxUtil.setBookList(bookList);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.main_pull_analysis:
try {
bookList=pullUtil.getBookList(is);
String content="";
for (Book book : bookList) {
content+=book.toString();
}
Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.main_pull_back_analysis:
try {
String str = pullUtil.setBookList(bookList);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
default:
break;
}
}
}
有个弊端,每次启动activity只能解析一次。