zoukankan      html  css  js  c++  java
  • 用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

  • 相关阅读:
    java io
    java 线程
    java 引用
    spring面试合集
    jvm(6):JMM
    jvm(n):JVM面试
    jvm(5):类加载机制
    jvm(4):类文件结构
    C# UDP编程简介
    Facebook 对 Memcache 伸缩性的增强
  • 原文地址:https://www.cnblogs.com/tianzhijiexian/p/4020250.html
Copyright © 2011-2022 走看看