用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="1">
        currentTag = localName;//input
        System.out.println("————开始解析"+qName+"这个标签了————");

        for (int i = 0; i < attributes.getLength(); i++) {
            String name = attributes.getLocalName(i);//第一次是:type
            String value = 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

时间: 2024-10-19 02:47:27

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

Java之Pull方式生成xml文件和解析xml文件

Pull XML解析器早已经被google集成到android sdk当中,它是google官方推荐的解析器. 如果我们要在Java桌面.J2ME等当中使用Pull方式生成xml文件和解析xml文件,需要用到kxml2: KXML解析器是基于普通XML PULL解析器的一个小巧的解析器,官网是http://kxml.org/ 普通XML PULL解析器的官网是http://xmlpull.org/ 实验开始: 在Eclipse中新建一个java项目,其中新建一个libs文件夹,拷贝从网上下载的k

Android学习(48) -- 获取xml文件并解析.

1.根据xml的格式创建JavaBean public class News { private String title; private String detail; private String comment; private String imageUrl; @Override public String toString() { return "News [title=" + title + ", detail=" + detail + ",

说说你知道的XML文件的解析方式,它们有什么区别

说说你知道的XML文件的解析方式,它们有什么区别 DOM(document object model)解析 将整个xml全部读到内存中去,形成树状结构. 优点:解析效率高,且可以对文档进行增删的操作 缺点:当xml文件很大,会导致内存溢出. SAX(Simple API for XML)解析 读取一行,解析一行,基于事件驱动 优点:不会造成内存溢出 缺点:解析效率较慢,且只能进行查阅的操作 原文地址:https://www.cnblogs.com/javaisbest/p/11588010.ht

php对xml文件的解析

近来较少写博客了,得克服懒惰的秉性啊! 今天研究了一下php对xml文件的解析. 用到了php的simplexml_load_file()方法,该方法会将xml文件生成一个SimpleXMLElement对象,该对象是继承了Traversable接口的对象,即可以像数组那样遍历其子集. 这样,我们就可以循环得到xml文件的内容,不多说废话,上例子. 假设有如下内容,名为test.xml的xml文件: <?xml version="1.0" encoding="utf-8

Android 5.0 system_fonts.xml文件的解析过程

Android 5.0 system_fonts.xml文件的解析过程 首先看看看5.0 中familyset version="22" 的格式 20 <family name="sans-serif"> 21 <font weight="100" style="normal">Roboto-Thin.ttf</font> 22 <font weight="100"

使用pull方式和sax方式对xml文件进行解析

这是我自己设置的xml文件 用一下方式获取它的内容 try{ OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() //对于虚拟机而言的本地地址 .url("http://10.0.2.2/get_data.xml") .build(); Response response =client.newCall(request).execute(); String responseD

SAX对xml文件的解析

package com.fortune; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import java.io.FileInputStream;import java.io.IOException;import

Android——xml文件的解析

解析方法:DOM.SAX.PULL DOM:将xml转化为树进行遍历 public void DOMParser() {     try {       // 1.创建DocumentBuilder实例       DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();        // 2.创建Document实例 Document doc = builder.parse(fil

曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de