android开发的对xml操作

来源:互联网 发布:网络.歌曲 编辑:程序博客网 时间:2024/06/05 09:02

android开发一般对xml操作常用三种技术:sax、dom、pull

分别详细的进行介绍:


首先创建开发测试坏境(一下三种方法都会使用这个环境):

在类路径下面创建xml文件:

person.xml

<?xml version="1.0" encoding="UTF-8"?>
<persons>
    <person id="1">
        <name>王昌龙</name>
        <age>23</age>
    </person>
   
    <person id="3">
        <name>小妾</name>
        <age>17</age>
    </person>
</persons>

(我的名字and因为没有媳妇,就假设叫小妾)

针对person创建javabean,(因为我们下面要以对象的形式获取此xml文件内容)

package cn.partner4java.xml.bean;

public class Person {
    private int id;
    private String name;
    private short age;
    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 short getAge() {
        return age;
    }
    public void setAge(short age) {
        this.age = age;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (id != other.id)
            return false;
        return true;
    }
   
}

sax:

SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接口。下面是一些ContentHandler接口常用的方法:
startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。
endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。
startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。
endElement(String uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。

不知道你看懂上面的解释了么?那好,我们动手做一下:

创建XMLContentHandler,如上面说的应该实现 ContentHandler接口,但是我们这里去集成一个它的实现类就OL,

package cn.partner4java.sax.service;

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 cn.partner4java.sax.bean.Person;

public class XMLContentHandler extends DefaultHandler {
    private List<Person> persons;
    private Person person;
    private String preTag;
   
    public List<Person> getPersons() {
        return persons;
    }
    //这个方法用来处理在XML文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,
    //使用new String(ch,start,length)就可以获取内容。
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        String data = new String(ch, start, length);
        if("name".equals(preTag)){
            person.setName(data);
        }else if("age".equals(preTag)){
            person.setAge(new Short(data));
        }
    }
    //这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if("person".endsWith(localName) && person != null){
            persons.add(person);
            person = null;
        }
        preTag = null;
       
    }
    //和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。
    @Override
    public void startDocument() throws SAXException {
        persons = new ArrayList<Person>();
    }

    //当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,
    //qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,
    //当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,
    //至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,
    //都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        //初始化:新建javabean
        if("person".equals(localName)){
            person = new Person();
            person.setId(new Integer(attributes.getValue(0)));
        }
        preTag = localName;
    }

   
}

 SAX 支持已内置到JDK1.5中,你无需添加任何的jar文件

书写service代码:

package cn.partner4java.sax.service;

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

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import cn.partner4java.sax.bean.Person;

public class SAXService {
   
    public static List<Person> readXML(InputStream inStream) {
           try {
                SAXParserFactory spf = SAXParserFactory.newInstance();
                SAXParser saxParser = spf.newSAXParser(); //创建解析器
                //设置解析器的相关特性,http://xml.org/sax/features/namespaces = true 表示开启命名空间特性 
                //saxParser.setProperty("http://xml.org/sax/features/namespaces",true);
                XMLContentHandler handler = new XMLContentHandler();
                saxParser.parse(inStream, handler);
                inStream.close();
                return handler.getPersons();
           } catch (Exception e) {
                e.printStackTrace();
           }
          return null;
        }

}

进行单元测试:

package cn.partner4java.sax.junit;


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

import android.test.AndroidTestCase;
import android.util.Log;


import cn.partner4java.sax.bean.Person;
import cn.partner4java.sax.service.SAXService;

public class SAXServiceTest extends AndroidTestCase {
    private static String TAG = "SAXServiceTest";
    public void testReadXML() {
        InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("person.xml");
        List<Person> persons = SAXService.readXML(inStream);
        for(Person person:persons){
            System.out.println(person.getName());
            Log.i(TAG, person.getName());
        }
    }

}

sax的集体操作大体就如上,没有什么太难理解的地方,我们重载的那些方法,你不要想着我们是如何去调用的,不是,他是使用的流程装载,也就说会安照我们开始解释的那个几个方法的作用,安步骤自动往下走,并且方法是重复调用的,因为我们xml里面不止一个数据。
android的单元测试我这里说一下:

因为单元测试也就是黑盒测试吧,有很好的作用,能够帮助我们去调试代码,避免出现很难解决的复杂错误,因为我们基本都是会每隔方法测试后,再继续往下写。

我们在写j2ee的junit是怎么样的我就不说了,

android里面首先,要配置我们的

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="cn.partner4java.sax"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".SaxDemoActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <uses-library android:name="android.test.runner" />
    </application>
    <instrumentation android:name="android.test.InstrumentationTestRunner"
          android:targetPackage="cn.partner4java.sax" android:label="Tests for My App" />
   

</manifest>

上面targetPackage指定的包要和应用的package相同。也就说必须相同,但是我们的单元测试可以放到此包的子包里,外面是不行的。

编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” )

你可能看到了我写了两种打印的方法:

System.out.println(person.getName());
Log.i(TAG, person.getName());

System.out.println在老版本的日志里面是不打印的(我现在用的最新版本可以了),所以只能用 Log。但是这两种打印方式还是都不可以打印中文,会出现乱码



DOM:

除了可以使用 SAX解析XML文件,大家也可以使用熟悉的DOM来解析XML文件。 DOM解析XML文件时,会将XML文件的所有内容读取到内存中,然后允许您使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来比较直观,并且,在某些方面比基于SAX的实现更加简单。但是,因为DOM需要将XML文件的所有内容读取到内存中,所以内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采用DOM是可行的。

DOM解析就很常见了,我也就不多说了,但是这个方法是不建议使用的,因为他会加载整个xml文件,会消耗更多的系统资源。

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

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


/**
 * 使用Dom解析xml文件
 *
 */
public class DomXMLReader {

public static List<Person> readXML(InputStream inStream) {
    List<Person> persons = new ArrayList<Person>();
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    try {
        DocumentBuilder builder = 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.getChildNodes();
            for (int j = 0; j < childsNodes.getLength(); j++) {
            Node node = (Node) childsNodes.item(j);             //判断是否为元素类型
            if(node.getNodeType() == Node.ELEMENT_NODE){                        Element childNode = (Element) node;
                                        //判断是否name元素
                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;
}


pull:

除了可以使用 SAX和DOM解析XML文件,大家也可以使用Android内置的Pull解析器解析XML文件。 Pull解析器的运行方式与 SAX 解析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型元素的值。
既然android建议我们使用pull,我就详细的说一下这个方法吧。

创建android工程,配置可以单元测试的环境,然后考入我们上面的javabean和xml文件

创建service:

package cn.partner4java.pull.service;

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

import cn.partner4java.pull.bean.Person;


public class PullService {
    public static List<Person> readXML(InputStream inStream) {
       
        return null;
    }
}

接下啦我们就书写readXML方法:

//获取xmlpull,我们可以使用android给我们提供的一个简单获取类(便于快速获得pull解析器)
        XmlPullParser parser = Xml.newPullParser();
        //进行编码设置,把我们需要解析的内容给他
        parser.setInput(inStream, "UTF-8");
        //获取事件,就可以触发第一解析到的字符对应的事件
            //他也是采用的流式触发
        int enentType = parser.getEventType();
        //这个方法是把流事件往后退
//            parser.next();
        List<Person> persons = null;
        Person person = null;
        //XmlPullParser.END_DOCUMENT:Logical end of the xml document. Returned from getEventType, next() and nextToken()
        //when the end of the input document has been reached.
        //就是判断这个事件不为文档末尾事件,我们就循环下去
        while(enentType != XmlPullParser.END_DOCUMENT){
            //对事件进行判断
            switch (enentType) {
            //是否为开始事件,可以进行数据初始化处理
            case XmlPullParser.START_DOCUMENT:
                persons = new ArrayList<Person>();
                break;
            //是否为开始元素事件,如<Persons>
                //再往下,处理到<person id...也会触发这个事件
                    //也就是每解析一个字符就会触发这个事件
            case XmlPullParser.START_TAG:
               
                String tag = parser.getName();
                //如果当前为person标签,我们需要获取id值
                if("person".equals(tag)){
                    person = new Person();
                    person.setId(new Integer(parser.getAttributeValue(0)));
                }else if(person != null){
                    if("name".equals(tag)){
                        //parser.nextText(),当前为name下一个节点,为文本
                        person.setName(parser.nextText());
                    }else if("age".equals(tag)){
                        person.setAge(new Short(parser.nextText()));
                    }
                }
               
               
                break;
             case XmlPullParser.END_TAG://结束元素事件
                 //如果解析到person结束元素事件时,我们就把当前的person加到结合中
                 if (parser.getName().equalsIgnoreCase("person") && person != null) {
                        persons.add(person);
                        person = null;
                    }

            }
            enentType = parser.next();
        }
        return persons;


然后我们对读方法进行单元测试

package cn.partner4java.pull.junit;

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

import cn.partner4java.pull.bean.Person;
import cn.partner4java.pull.service.PullService;
import android.test.AndroidTestCase;

public class PullServiceTest extends AndroidTestCase {
   
    public void readXML() throws Exception{
        InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("person.xml");
        List<Person> persons = PullService.readXML(inStream);
        for(Person person:persons){
            System.out.println(person.getId() + ":" + person.getName());
        }
    }
}

使用Pull解析器生成XML文件:

有些时候,我们需要生成一个XML文件,生成XML文件的方法有很多,如:可以只使用一个StringBuilder组拼XML内容,然后把内容写入到文件中;或者使用DOM API生成XML文件,或者也可以使用pull解析器生成XML文件,这里推荐大家使用Pull解析器。

 public static void writeXML(List<Person> persons, Writer writer) throws IllegalArgumentException, IllegalStateException, IOException{
  XmlSerializer serializer = Xml.newSerializer();
  serializer.setOutput(writer);
  //开始文档
  serializer.startDocument("UTF-8", true);
  //设置开始标签
  serializer.startTag(null, "persons");
  for(Person person:persons){
   serializer.startTag(null, "person");
   serializer.attribute("", "id", String.valueOf(person.getId()));
   
   serializer.startTag("", "name");
   serializer.text(person.getName());
   serializer.endTag("", "name");
   
   serializer.startTag("", "age");
   serializer.text(String.valueOf(person.getAge()));
   serializer.endTag("", "age");
   
   serializer.endTag(null, "person");
  }
  
  //结束标签
  serializer.endTag(null, "persons");
  //结束文档
  serializer.endDocument();
  
 }

大体上就是这个样子,是和xml文件相对象的,一个开始标签,一个结束标签。

单元测试:

public void writeXML() throws IllegalArgumentException, IllegalStateException, IOException{
  List<Person> persons = new ArrayList<Person>();
  persons.add(new Person(3, "王昌龙", (short)23));
  persons.add(new Person(4, "王昌龙的小妾", (short)17));
  File file = new File(Environment.getExternalStorageDirectory(),"person.xml");
  FileOutputStream fileOutputStream = new FileOutputStream(file);
  Writer writer = new OutputStreamWriter(fileOutputStream , "UTF-8");
  PullService.writeXML(persons, writer);
  writer.flush();
  writer.close();
 }

有一个问题,不知道你发现了没有,往SD卡写数据是要打开权限的,但是我确实没打开,也生成文件了。

<!-- 在SDCard中创建与删除文件权限 -->
 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
 <!-- 往SDCard写入数据权限 -->
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>