Android 创建与解析XML(一)---- SAX方式

来源:互联网 发布:淘宝上卖视频教程 编辑:程序博客网 时间:2024/05/14 15:04

在Android中,常见的解析XML文档的方式有:SAX解析器,DOM解析器,PULL解析器。

 

先了解一下SAX解析的方法。

SAX解析器:

SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。

SAX解析器优点:

1、顺序读入文档并产生相应事件,可以处理任何大小的XML文档

2、解析速度快,占用内存少

3、对开发人员而言更灵活,可以用SAX创建自己的XML对象模型

SAX解析器缺点:

1、只能对文档按顺序解析一遍,不支持对文档的随意访问

2、只能读取XML文档内容,而不能修改

3、开发上比较复杂,需要自己来实现事件处理器

先在项目的assets目录中放置一个XML文档books.xml,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<books>
 <book>
  <id>1</id>
  <name>Android 学习精要</name>
  <price>79.00</price>
 </book>
 <book>
  <id>2</id>
  <name>Android 应用开发揭秘</name>
  <price>69.00</price>
 </book>
</books>

 

采用SAX解析器的具体处理步骤:

1、创建SAXParserFactory对象

2、根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器

3、实例化一个DefaultHandler对象

4、调用SAXParser的parse方法从输入源中获取到的xml数据

5、通过DefaultHandler返回我们需要的数据集合

 

重点在于DefaultHandler对象中对每一个元素节点,属性,文本内容,文档内容进行处理。

前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1、当导航到文档开始标签时,在回调函数startDocument中,可以不做处理,当然你可以验证下UTF-8、实例化一个集合用来存贮list等等。

2、当导航到books开始标签时,在调用方法startElement。

3、导航到book开始标签时,就说明需要实例化Book对象了,当然book标签也可以有属性,如果有实例化Book后还必须取出属性值,attributes.getValue(NAME),同时赋予book对象中,同时添加为导航到的book标签添加一个boolean为真的标识,用来说明导航到了book元素。

4、当然有book标签内还有子标签(节点),但是SAX解析器是不知道导航到什么标签的,它只懂得开始,结束而已。那么如何让它认得我们的各个标签呢?当然需要判断了,于是可以使用回调方法startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就可以了。我们还必须让SAX知道,现在导航到的是某个标签,因此添加一个true属性让SAX解析器知道。

5、它还会导航到文本内标签,(就是<name></name>里面的内容),回调方法characters,我们一般在这个方法中取出就是<name></name>里面的内容,并保存。

6、当然它是一定会导航到结束标签</book> 或者</books>的,如果是</book>标签,记得把book对象添加进list中。如果是book中的子标签</name>,就将取得的值添加到book的name属性中。

 

Book.java

public class Book {

 private int id;
 private String name;
 private float 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 [id=" + id + ", name=" + name + ", price=" + price + "]";
 }
 
}

先自定义一个解析器接口BookParser.java

/**
 *
 */
package com.sym.xml;

import java.io.InputStream;
import java.util.List;

/**
 * @author sym
 *
 */
public interface BookParser {

 /**
  * 解析输入流,得到Book对象集合
  * @param is
  * @return
  * @throws Exception
  */
 public List<Book> parser(InputStream is) throws Exception;
 
 /**
  * 序列化Book对象,得到XML形式的字符串
  * @param books
  * @return
  * @throws Exception
  */
 public String serialize(List<Book> books) throws Exception;
}

 

SaxBookParser.java

package com.sym.xml;

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
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.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;

public class SaxBookParser implements BookParser {

 @Override
 public List<Book> parser(InputStream is) throws Exception {
  SAXParserFactory factory = SAXParserFactory.newInstance();//取得SAXParserFactory实例
  SAXParser parser = factory.newSAXParser();//从factory获取SAXParser实例
  MyHandler handler = new MyHandler();
  parser.parse(is, handler);
  return handler.getBooks();
 }

 @Override
 public String serialize(List<Book> books) throws Exception {
  SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();//取得SAXTransformerFactory实例
  TransformerHandler handler = factory.newTransformerHandler();//从factory获取TransformerFactory实例
  Transformer transformer = handler.getTransformer();//从handler获取Transformer实例
  transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");//设置输出采用的编码方式
  transformer.setOutputProperty(OutputKeys.INDENT, "yes");//是否自动添加额外的空白
  transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");//是否忽略XML声明
  
  StringWriter writer = new StringWriter();
  Result result = new StreamResult(writer);
  handler.setResult(result);
  
  String uri = "";//代表命名空间的URI,当URI无值时,须设置为空字符串
  String localName = "";//命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时,须设置为空字符串
  
  handler.startDocument();
  handler.startElement(uri, localName, "books", null);
  AttributesImpl attrs = new AttributesImpl(); //负责存放元素的属性信息
  char[] ch = null;
  for(Book book : books){
   attrs.clear();//清空属性列表
   attrs.addAttribute(uri, localName, "id", "string", String.valueOf(book.getId()));//添加一个名为id的属性(type影响不大,这里设为string)
   handler.startElement(uri, localName, "book", attrs);//开始一个book元素 关联上面设定的id属性
   handler.startElement(uri, localName, "name", null);//开始一个name元素 没有属性
   ch = book.getName().toCharArray() ;
   handler.characters(ch, 0, ch.length);//设置name元素的文本节点
   handler.endElement(uri, localName, "name");
   handler.startElement(uri, localName, "price", null);//开始一个price元素 没有属性
   ch = String.valueOf(book.getPrice()).toCharArray() ;
   handler.characters(ch, 0, ch.length);//设置price元素的文本节点
   handler.endElement(uri, localName, "price");
   handler.endElement(uri, localName, "book");
  }
  handler.endElement(uri, localName, "books");
  handler.endDocument();
  return writer.toString();
 }

 private class MyHandler extends DefaultHandler{
  private List<Book> books;
  private Book book;
  private StringBuilder builder;
  
  /**
   * 返回解析后得到的Book对象集合
   * @return
   */
  public List<Book> getBooks(){
   return books;
  }
  
  @Override
  public void startDocument() throws SAXException {
   // TODO Auto-generated method stub
   super.startDocument();
   books = 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();
   }
   builder.setLength(0);//将字符长度设置为0,以便重新开始读取元素内的字符节点
  }

  @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);//将读取的字符数组追加到builder中
  }

  @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.setId(Integer.parseInt(builder.toString()));
   }else if(localName.equals("name")){
    book.setName(builder.toString());
   }else if(localName.equals("price")){
    book.setPrice(Float.parseFloat(builder.toString()));
   }else if(localName.equals("book")){
    books.add(book);
   }
  }

 }
}

 

MainActivity.java

package com.sym.xml;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

 private static final String TAG = "XML";
 private BookParser parser;
 private List<Book> books;
 private TextView readxml, writerxml;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  readxml = (TextView) this.findViewById(R.id.readxml);
  writerxml = (TextView) this.findViewById(R.id.writerxml);
 }
 
 public void read(View v){
  try {
   InputStream is = getAssets().open("books.xml");
   parser = new SaxBookParser();//创建SaxBookParser实例
//   parser = new DomBookParser(); 
//   parser = new PullBookParser();
   books = parser.parser(is);//解析输入流
   StringBuffer sb = new StringBuffer();
   sb.append("解析XML结果:" + "\n");
   for(Book book : books){
    sb.append(book.toString() + "\n");
    Log.i(TAG, book.toString());
   }
   readxml.setText(sb.toString());
  } catch (Exception e) {
   Log.e(TAG, e.getMessage());
  }
  
 }
 
 public void writer(View v){
  try {
   if(parser != null){
    String xml = parser.serialize(books);
    FileOutputStream fos = openFileOutput("books.xml", Context.MODE_PRIVATE);
    fos.write(xml.getBytes("UTF-8"));
    StringBuffer sb = new StringBuffer();
    sb.append("创建XML结果:"+"\n");
    sb.append(xml);
    writerxml.setText(sb.toString());
   }else{
    Toast.makeText(this, "请先解析XML!", Toast.LENGTH_LONG).show();
   }
   
  } catch (Exception e) {
   Log.e(TAG, e.getMessage());
  }
 }

}

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"
     >

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="解析XML"
        android:onClick="read" />
   
    <TextView
        android:id="@+id/readxml"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="解析XML结果:"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="创建XML"
        android:onClick="writer" />

    <TextView
        android:id="@+id/writerxml"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="创建XML结果:"
        />
   
</LinearLayout>

 

 


得到的结果图,如下:

0 0