首页 > 代码库 > 用SAX和PULL进行XML文件的解析与生成

用SAX和PULL进行XML文件的解析与生成

XML解析有传统的dom方法还有Jsoup,SAX,PULL等,这里讲的是比较省内存的SAX和PULL方法。Android中极力推荐用PULL的方式来解析,我个人觉得pull确实比较简单,但其内部的逻辑性不是很分明。所以今天做了个类来将其中的多个步骤进行了分割,以后直接拿来用即可。

1.SAX:

首先先讲解SAX中各个方法的作用:

我们以这个不规则的xml语句做例子:

<abc:kale sex=m age=21>jack</abc:kale>

 

startDocument:开始解析一个xml文件时触发

endDocument:这个xml文件被解析完毕时触发

startElement:开始解析xml文件中的一个标签时触发,这里可以得到标签名和其中的各个属性值。

如:从<person age = 12 sex = f/>会得到标签名:【person】和属性值:【age = 12 sex = f】

endElement:结束解析一个标签时触发

characters:解析这个标签内部的内容时触发,这里可以得到这个标签子节点中的内容。

如:从<name>jack<name>中得到【jack】

 

下面是实现代码:

1.首先建立一个SAX对象,然后进行解析工作。这里会要自己建立一个ContentHandler的子类

/**     * 通过sax进行解析     * @param str     */    public void sax(String str) {        //下面是固定的写法        SAXParserFactory factory = SAXParserFactory.newInstance();        XMLReader reader;        try {            //得到xmlReader对象            reader = factory.newSAXParser().getXMLReader();            //设置内容处理器            reader.setContentHandler(new MyContentHandler());            reader.parse(new InputSource(new StringReader(str)));        } catch (SAXException | ParserConfigurationException | IOException e) {            // TODO 自动生成的 catch 块            e.printStackTrace();        }    }

 

MyContentHandler.java 这个类就是来处理事务的,里面有各种回调方法

package com.kale.xml;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;/** * @author:Jack Tony * @tips  :举个极端的例子:<input type="hidden" name="UserType">kale</input> *     startElement中可以得到的是:type="hidden" name="UserType" *     characters中得到的是:kale * @date  :2014-10-11 */public class MyContentHandler extends DefaultHandler{    //当前正在解析的标签名    private String currentTag;    /*      * 开始解析这个xml文件的时候触发     */    @Override    public void startDocument() throws SAXException {        System.out.println("开始解析这个文件了");    }        /*      * 结束解析这个xml文件的时候触发     */    @Override    public void endDocument() throws SAXException {        System.out.println("文件解析结束");    }        /*      * 开始解析每个元素的时候触发     * <person age = 12 sex = f/>     * <kale:name>jack<kale:name>     * 1.uri:当前正在解析的元素的命名空间     * 2.localName:不带前缀的这个元素的名字——>name     * 3.qName:带前缀的这个元素命——>kale:name     * 4.attributes:得到的元素中的属性——>age=12 set=f     */    @Override    public void startElement(String uri, String localName, String qName,            Attributes attributes) throws SAXException {        //举例:<input type="hidden" name="UserType" id="UserType" value="http://www.mamicode.com/1">        currentTag = localName;//input        System.out.println("————开始解析"+qName+"这个标签了————");        for (int i = 0; i < attributes.getLength(); i++) {            String name = attributes.getLocalName(i);//第一次是:type            String value = http://www.mamicode.com/attributes.getValue(i);//第一次是:hidden            System.out.println(name + " = " + value);        }            }        /* (非 Javadoc)     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)     * 停止解析这个元素的时候触发     */    @Override    public void endElement(String uri, String localName, String qName)            throws SAXException {        // TODO 自动生成的方法存根        super.endElement(uri, localName, qName);        System.out.println("————-解析"+qName+"标签结束————");    }        /*      * 得到元素中的内容,比如下面的jack     * <name>jack<name>     */    @Override    public void characters(char[] ch, int start, int length)            throws SAXException {        //举例:<name>jack<name>        if (currentTag.equals("name")) {            System.out.println("name = " + new String(ch,start,length));//会输出jack        }        if (currentTag.equals("age")) {            System.out.println("age = " + new String(ch,start,length));//会输出21        }    }}

 

贴上测试样本(由于xml文件可能是不规范的,所以处理时要考虑异常):

<?xml version="1.0" encoding="utf-8"?><namespace xmlns:abc="http://schemas.android.com/apk/res/android">    <form id="form1" name="form1" method="post" action="ok" style="margin: 0; padding: 0;" >        <input type="hidden" name="UserType" id="12" value="1"/>        <abc:name>jack</abc:name>        <abc:age>21</abc:age>    </form></namespace>

 

测试结果:

 

2.PULL

其实PULL中就一个重要的方法XmlPullParser.next();,正因如此才让其变得简单很多。PULL的特点是运行到什么状态是没有回调方法的,它进行某个处理状态时,会改变一个状态变量,通过getEventType()就可以来判断当前是处于什么状态了。

推荐浏览:http://384444165.iteye.com/blog/1521332

但正因为内部逻辑需要开发者来处理,所以变得结构不是很清晰。我这里通过一个类来将其转换为SAX的框架,以后只需要复写这些方法便可以直接进行操作了。至于运行到哪一步,看方法名酒明白了。同样还是之前的那幅图:

 

 

这里面的方法的作用也是和SAX一样的。下面是使用的代码:

1.建立这个类的对象,执行操作

    /**     * 通过pull进行解析     * @param str     */    public void pull(String str) {        XmlPullParser parser = Xml.newPullParser();        InputStream in = new ByteArrayInputStream(str.getBytes());        try {            parser.setInput(in,"utf-8");            MyXmlPullParserTask task = new MyXmlPullParserTask(parser);            task.execute();//开始解析文件        } catch (XmlPullParserException e) {            e.printStackTrace();        }    }

 

2.进行解析处理

MyXmlPullParserTask.java

我通过前面的switch-case语句将处理的状态进行了分割,这些状态可以完全类比到SAX中。这样便于理解!

package com.kale.xml;import java.io.IOException;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;public class MyXmlPullParserTask {     /**     * 当前正在解析的标签名,类似于sax中的localName     * 可以得到<abc:kale sex=m age=21>jack</abc:kale>中【kale】     */    private String currentTag;        /**     * 当前正在解析的标签名的前缀,sax中的qName=前缀+当前标签名     * 可以得到<abc:kale sex=m age=21>jack</abc:kale>中【abc】     */    private String currentPrefix;        /**     * 当前正在解析的标签的命名空间,类似于sax中的uri     * 得到     *     <namespace xmlns:abc="http://schemas.android.com/apk/res/android">     *     <abc:kale sex=m age=21>jack</abc:kale>     * 中的【http://schemas.android.com/apk/res/android*/    private String currentNamespace;        private XmlPullParser parser;    public MyXmlPullParserTask(XmlPullParser parser) {        this.parser = parser;    }        /**     * 开始解析的方法,这里已经写好了,尽量不要该这里的代码。     */    public void execute() {        try {            // 得到当前状态的标识代码            int eventCode = parser.getEventType();            // 如果当前状态不是文档结束,那么就继续循环            boolean flag = true;            while (flag) {                // 当前解析元素的标签,不带前缀                currentTag = parser.getName();                currentNamespace = parser.getNamespace();                currentPrefix = parser.getPrefix();                                switch (eventCode) {                case XmlPullParser.START_DOCUMENT:                    startDocument();                    break;                case XmlPullParser.END_DOCUMENT:                    endDocument();                    flag = false;// 到文档末尾了,结束循环                    break;                case XmlPullParser.START_TAG:                    startElement(parser);                    characters(parser);                    break;                case XmlPullParser.END_TAG:                    endElement(parser);                    break;                default:                    break;                }                eventCode = parser.next();            }        } catch (XmlPullParserException | IOException e) {            e.printStackTrace();        }    }    /**     * 开始解析文件时触发的方法     */    public void startDocument() {        System.out.println("开始解析这个文件了");    }    /**     * 结束解析这个xml文件的时候触发     */    public void endDocument() {        System.out.println("该文件解析完成");    }    /**     * 开始解析某个标签时触发     * 可以得到<abc:kale sex=m age=21>jack</abc:kale>中【sex=m age=21】部分     * @param parser     */    public void startElement(XmlPullParser parser) {        System.out.println("————开始解析"  + currentPrefix +":"+ currentTag + "这个标签了————");        for (int i = 0; i < parser.getAttributeCount(); i++) {            String name = parser.getAttributeName(i);            String value = parser.getAttributeValue(i);            System.out.println(name + " = " + value);        }    }    /**     * 结束解析某个标签时触发     * 遇到/>时表示一个标签解析完成,而遇到</xxx>不会触发     * @param parser     */    public void endElement(XmlPullParser parser) {        System.out.println("————解析" + currentPrefix +":"+ currentTag + "标签结束————");    }    /**     * 解析标签中内容时触发     * 得到<name>jack</name>中【jack】的部分     * @param parser     * @throws XmlPullParserException     * @throws IOException     */    public void characters(XmlPullParser parser) throws XmlPullParserException, IOException {        if (currentTag.equals("name")) {            System.out.println("name = " + parser.nextText());// 会输出jack        } else if (currentTag.equals("age")) {            System.out.println("age = " + parser.nextText());// 会输出21        }    }}

 

测试样本:

<?xml version="1.0" encoding="utf-8"?><namespace xmlns:abc="http://schemas.android.com/apk/res/android">    <form id="form1" name="form1" method="post" action="ok" style="margin: 0; padding: 0;" >        <input type="hidden" name="UserType" id="12" value="1"/>        <abc:name>jack</abc:name>        <abc:age>21</abc:age>    </form></namespace>

 

测试结果:

3.XML文件的生成

生成是用简单的pull来做的,没啥技术含量,就是用代码来写xml,最后放到sd卡中

    /**     * 建立一个xml文件     */    public void creatXML() {        XmlSerializer serializer = Xml.newSerializer();        File file = new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml");        FileOutputStream fos = null;        try {            fos = new FileOutputStream(file);            serializer.setOutput(fos, "UTF-8");                        serializer.startDocument("UTF-8", true);            //命名空间+标签名,命名空间可以=null            serializer.startTag(null, "namespace");            serializer.attribute("http://schemas.android.com/apk/res/android", "abc", "egf");                        serializer.startTag(null, "persons");            for (int i = 0; i < 2; i++) {                serializer.startTag(null, "person");                serializer.attribute(null, "id", i+1+"");                serializer.attribute(null, "age", i+10+"");                                serializer.startTag(null, "name");                serializer.text("jack");                serializer.endTag(null, "name");                                serializer.endTag(null, "person");            }            serializer.endTag(null, "persons");            serializer.endTag(null, "namespace");            serializer.endDocument();                    } catch (IllegalArgumentException | IllegalStateException | IOException e) {            // TODO 自动生成的 catch 块            e.printStackTrace();        }        finally {            try {                fos.flush();                fos.close();            } catch (IOException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }                    }    }

 

测试结果:

 

全部activity中的代码

package com.kale.xml;import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.StringReader;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParserFactory;import org.xml.sax.InputSource;import org.xml.sax.SAXException;import org.xml.sax.XMLReader;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;import org.xmlpull.v1.XmlSerializer;import android.app.Activity;import android.os.Bundle;import android.os.Environment;import android.util.Xml;import android.view.View;import android.widget.Toast;public class MainActivity extends Activity {        @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        creatXML();        Toast.makeText(this, "xml文件建立成功,在SD卡根目录下sharpandroid.xml", 0).show();    }        public void buttonListener(View v) {        //从assets文件夹中得到test.xml文件的内容        String str = getFromAssets("test.xml");        switch (v.getId()) {        case R.id.sax_button:            //通过sax进行文件的解析            sax(str);            break;        case R.id.pull_button:            //通过pull来解析文件            pull(str);            break;        default:            break;        }    }        /**     * 通过sax进行解析     * @param str     */    public void sax(String str) {        //下面是固定的写法        SAXParserFactory factory = SAXParserFactory.newInstance();        XMLReader reader;        try {            //得到xmlReader对象            reader = factory.newSAXParser().getXMLReader();            //设置内容处理器            reader.setContentHandler(new MyContentHandler());            reader.parse(new InputSource(new StringReader(str)));        } catch (SAXException | ParserConfigurationException | IOException e) {            // TODO 自动生成的 catch 块            e.printStackTrace();        }    }        /**     * 通过pull进行解析     * @param str     */    public void pull(String str) {        XmlPullParser parser = Xml.newPullParser();        InputStream in = new ByteArrayInputStream(str.getBytes());        try {            parser.setInput(in,"utf-8");            MyXmlPullParserTask task = new MyXmlPullParserTask(parser);            task.execute();//开始解析文件        } catch (XmlPullParserException e) {            e.printStackTrace();        }    }          /**     * @param fileName     * @return assets中文件的字符串     */    public String getFromAssets(String fileName){           String result="";          try {               InputStreamReader inputReader = new InputStreamReader( getResources().getAssets().open(fileName) );               BufferedReader bufReader = new BufferedReader(inputReader);              String line="";              while((line = bufReader.readLine()) != null) {                  result += line;              }          } catch (Exception e) {               e.printStackTrace();           }          return result;      }        /**     * 建立一个xml文件     */    public void creatXML() {        XmlSerializer serializer = Xml.newSerializer();        File file = new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml");        FileOutputStream fos = null;        try {            fos = new FileOutputStream(file);            serializer.setOutput(fos, "UTF-8");                        serializer.startDocument("UTF-8", true);            //命名空间+标签名,命名空间可以=null            serializer.startTag(null, "namespace");            serializer.attribute("http://schemas.android.com/apk/res/android", "abc", "egf");                        serializer.startTag(null, "persons");            for (int i = 0; i < 2; i++) {                serializer.startTag(null, "person");                serializer.attribute(null, "id", i+1+"");                serializer.attribute(null, "age", i+10+"");                                serializer.startTag(null, "name");                serializer.text("jack");                serializer.endTag(null, "name");                                serializer.endTag(null, "person");            }            serializer.endTag(null, "persons");            serializer.endTag(null, "namespace");            serializer.endDocument();                    } catch (IllegalArgumentException | IllegalStateException | IOException e) {            // TODO 自动生成的 catch 块            e.printStackTrace();        }        finally {            try {                fos.flush();                fos.close();            } catch (IOException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }                    }    }  }

 

布局文件:

<RelativeLayout 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"    tools:context="${relativePackage}.${activityClass}" >    <Button        android:id="@+id/sax_button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_alignParentTop="true"        android:text="通过SAX来解析"         android:onClick="buttonListener"/>    <Button        android:id="@+id/pull_button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_alignParentTop="true"        android:text="通过PULL来解析"         android:onClick="buttonListener"/></RelativeLayout>

 

源码下载:http://download.csdn.net/detail/shark0017/8028375

 

用SAX和PULL进行XML文件的解析与生成