SAX解析及生成XML
上一篇写了DOM方式解析及生成XML,次篇来说一下SAX方式解析及生成XML,DOM和SAX方式都是Java原生支持的,不需要导入第三方Jar包。不同点是DOM是一次性加载XML到内存,而SAX方式是逐条读取解析。二者的优势和劣势自然也就显现了,DOM的优势在于由于XML是整个加载到内存的,便于解析和修改,缺点是在XML文件比较大的时候,很可能造成内存泄漏,比如解析一个十几兆几十兆的文件。SAX是基于事件的,由于是逐条解析,每到一个点会触发Handler中的对应方法,所以内存消耗较小,缺点是编码不方便(从代码中可以看出来,相较于DOM,存储实例不太方便),并且无法像DOM方式一样实现解析并修改,而且也不能实现访问XML的任意一个位置,必须一步步走。

下面简略归纳一下SAX解析的一般步骤:
(1)通过SAXParserFactory的静态方法获取SAXParserFactory实例;
(2)获取SAXParser(通过SAXParserFactory实例) ;
(3)调用SAXParse的parse方法开始解析,此处parse方法需要传入一个DefaultHandler实例,所以需要自己建立一个类继承自DefaultHandler,XML解析的相关函数就在此类内部实现,常用的方法:startDocument(),startElement(), character(); 有start方法就有end方法: endDocument(), endElement()等。以上就是每个事件触发的方法,很容易理解,节点的开始与结束。


下面简要归纳一下SAX生成XML的步骤:
既然是基于事件驱动的,那必然这里也会是一些事件。
(1) 通过静态方法获取SAXTransformerFactory实例;
(2) 获取TransformerHandler实例;
(3) 为设置生成的XML的属性,如换行、编码等,还需获取Transformer实例。
(4) 新建一个StreamResult对象,通过TransformerHandler的setResult方法将文件与将要生成的节点树关联起来.(注意,对于Transformer的定制必须在setResult调用之前,否则XML相关属性的设置将无效);
(5) 然后就是对应事件的调用,比如startDocument、startElement、character等方法。


话不多说,看代码比较直观:
此处同样需要用到Language.xml文件:
< xml version="1.0" encoding="utf-8" >
<language cat="IT" count="3">
    <lan id="1">
        <name>Java</name>
        <IDE>eclipse</IDE>
        <filename_extension>.java</filename_extension>
    </lan>
    <lan id="2">
        <name>C++</name>
        <IDE>Cline</IDE>
        <filename_extension>.cpp</filename_extension>
    </lan>
    <lan id="3">
        <name>PHP</name>
        <IDE>PhpStorm</IDE>
        <filename_extension>.php</filename_extension>
        <description>the best language in the world.. </description>
    </lan>
</language>

然后是language实体:Language.java
package org.devsong.entity;

public class Language {
    private String name;
    private long id;
    private String IDE;
    private String nameExt;
    private String description;

    public Language() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getIDE() {
        return IDE;
    }

    public void setIDE(String iDE) {
        IDE = iDE;
    }

    public String getNameExt() {
        return nameExt;
    }

    public void setNameExt(String nameExt) {
        this.nameExt = nameExt;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Language [name=" + name + ", id=" + id + ", IDE=" + IDE + ", nameExt=" + nameExt + ", description=" + description
                + "]";
    }
}

之后是解析要用到的Handler类,MyHandler类,其中定义了一个私有的ArrayList用于存储Language对象。代码如下:
package org.devsong.myhandler;

import java.util.ArrayList;

import org.devsong.entity.Language;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class MyHandler extends DefaultHandler {

    private ArrayList list = new ArrayList<>();
    private Language listEle = null;         //Language存储缓存
    private String TypedEle = "lan";
    private String content;          //节点内容缓存

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals(TypedEle)) {
            System.out.println();       //为便于观察,若当前结点名为分类节点,则输出换行
            if (listEle == null) {
                listEle = new Language();     //若listEle为空,则创建对象
            }
        }
        System.out.print(qName + ":");
        if (attributes != null) {
            for (int i = 0; i < attributes.getLength(); i++) {
                System.out.print("[attr]" + attributes.getQName(i) + ":" + attributes.getValue(i) + "   ");
                //若节点名等于分类节点,且当前属性名等于id,则将id属性设置到language实体
                if (qName.equals(TypedEle) && attributes.getQName(i).equals("id")) {
                    listEle.setId(Long.parseLong(attributes.getValue(i)));
                }
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if (qName.equals(TypedEle)) {     //若遍历完一个分类节点,则将分类节点加入列表
            list.add(listEle);
            listEle = null;
        }else{         //否则继续进行节点属性存储操作
            store(qName);
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {    //此函数获取的是节点内容
        super.characters(ch, start, length);
        String ContentTemp = new String(ch, start, length);
        if (!"".equals(ContentTemp.trim())) {       //剔除干扰内容: 空白文本
            content = ContentTemp;
            System.out.print(ContentTemp + "   ");
        }
    }

    /**
     * 实体成员属性存储
     * @param qName
     */
    public void store(String qName) {
        if ("name".equals(qName)) {
            listEle.setName(content);
        } else if ("IDE".equals(qName)) {
            listEle.setIDE(content);
        } else if ("filename_extension".equals(qName)) {
            listEle.setNameExt(content);
        } else if("description".equals(qName)) {
            listEle.setDescription(content);
        }
    }
    
    public ArrayList getList(){
        return this.list;
    }

}

最后是测试代码及生成XML的逻辑:SAXTest.java
package org.devsong.saxtest;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.ParserConfigurationException;
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.TransformerConfigurationException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.devsong.entity.Language;
import org.devsong.myhandler.MyHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

public class SAXTest {

    private ArrayList list = null;
    private Language listEle = null;

    public static void main(String[] args) {
        SAXTest test = new SAXTest();
        test.startParse();
        test.genXML("./src/res/language_sax.xml");
    }

    private void startParse() {
        try {
            //获取SAXParserFactory实例
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //获取SAXParser实例
            SAXParser parser = factory.newSAXParser();
            //创建MyHandler对象准备解析
            MyHandler handler = new MyHandler();
            //开始解析
            parser.parse("./src/res/language.xml", handler);
            list = handler.getList();
            //输出实体列表以便观察存储结果
            System.out.println("\n\n==================== entity ===================");
            for (Language lan : list) {
                System.out.println(lan);
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
    *生成XML文件
    */
    private void genXML(String uri) {
        SAXTransformerFactory tff = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
        try {
            //获取TransformerHandler实例
            TransformerHandler tfh = tff.newTransformerHandler();
            //获取Transformer实例并设置XML相关属性
            Transformer tf = tfh.getTransformer();
            tf.setOutputProperty(OutputKeys.ENCODING, "utf-8");
            tf.setOutputProperty(OutputKeys.INDENT, "yes");
            File file = new File(uri);
            Result result = new StreamResult(new FileOutputStream(file));
            tfh.setResult(result);
            
            //XML节点树生成过程
            tfh.startDocument();
            AttributesImpl atts = new AttributesImpl();
            atts.addAttribute("", "", "CAT", "", "IT");
            atts.addAttribute("", "", "count", "", "3");
            tfh.startElement("", "", "language", atts);
            if (list != null) {
                for (int i = 0; i < list.size(); i++) {
                    listEle = list.get(i);
                    atts.clear();       //每次需要使用atts时需要清除上次保留的内容
                    atts.addAttribute("", "", "id", "", listEle.getId() + "");
                    tfh.startElement("", "", "lan", atts);
                    atts.clear();    //以下节点没有属性,所以只需要这里clear一下即可,当然,为保险也可以每个if语句块内添加一个clear语句
                    if (listEle.getName() != null) {
                        tfh.startElement("", "", "name", atts);
                        tfh.characters(listEle.getName().toCharArray(), 0, listEle.getName().length());
                        tfh.endElement("", "", "name");
                    }
                    if (listEle.getIDE() != null) {
                        tfh.startElement("", "", "IDE", atts);
                        tfh.characters(listEle.getIDE().toCharArray(), 0, listEle.getIDE().length());
                        tfh.endElement("", "", "IDE");
                    }
                    if (listEle.getNameExt() != null) {
                        tfh.startElement("", "", "filename_extension", atts);
                        tfh.characters(listEle.getNameExt().toCharArray(), 0, listEle.getNameExt().length());
                        tfh.endElement("", "", "filename_extension");
                    }
                    if (listEle.getDescription() != null) {
                        tfh.startElement("", "", "name", atts);
                        tfh.characters(listEle.getDescription().toCharArray(), 0, listEle.getDescription().length());
                        tfh.endElement("", "", "name");
                    }
                    tfh.endElement("", "", "lan");
                }
                tfh.endElement("", "", "language");
            }
            tfh.endDocument();    //注意end和start是对应的
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

}

到这里可以看到,对比DOM方式,生成XML文件方式的带码结构还是类似的,只要紧紧围绕“树模型”就可以发现结构都是一样的。执行结果如下:
language:[attr]cat:IT   [attr]count:3   
lan:[attr]id:1   name:Java   IDE:eclipse   filename_extension:.java   
lan:[attr]id:2   name:C++   IDE:Cline   filename_extension:.cpp   
lan:[attr]id:3   name:PHP   IDE:PhpStorm   filename_extension:.php   description:the best language in the world..    

==================== entity ===================
Language [name=Java, id=1, IDE=eclipse, nameExt=.java, description=null]
Language [name=C++, id=2, IDE=Cline, nameExt=.cpp, description=null]
Language [name=PHP, id=3, IDE=PhpStorm, nameExt=.php, description=the best language in the world.. ]
可见解析过程没有问题,另外,打开新生成的XML文件与源文件做对比可以发现XML文件生成无误。
It's
欢迎访问本站,欢迎留言、分享、点赞。愿您阅读愉快!
*转载请注明出处,严禁非法转载。
https://www.devsong.org
QQ留言 邮箱留言
头像
引用:
取消回复
提交
涂鸦
涂鸦
热门