欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SAX解析XML文件

程序员文章站 2022-03-03 15:06:24
...

前言:在进入正题之前,我觉得很有必要让大家回顾一下最常用的XML文件解析的两种方式:SAX解析 和 DOC解析

1. SAX解析 (Simple API for XML)

定义:SAX是Simple API for XML的缩写,它是事件驱动的,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。

优点:1. 解析可以立即开始,解析速度快,数据量大的xml建议使用SAX解析提高解析速度。

           2. 占用内存小,没有内存压力

缺点:不能对结点做修改

适用:读取XML文件

总结:我个人理解SAX解析就是一种懒解析,类似于懒加载机制一样。

 

2. DOC解析 (Document Object Model)

定义:DOM将XML文档作为一个树形结构,而树叶被定义为为节点。根据DOM,XML文档中的每个成分都是一个节点

优点:把XML文件在内存中构建属性结构,可以遍历和修改节点。

缺点:如果文件比较大,内存有压力,解析的时间会比较长。

适用:修改XML数据

总结:DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点)

 

3. SAX解析和DOC解析的区别

  1. SAX解析速度快,占用内存资源小,可以快速读取XML文件,多适用于文件比较大的XML
  2. DOC解析速度慢,占用内存资源大,可以修改XML文件数据,多适用于文件比较小的XML

以上概念借用了其他大佬的博客总结出来的,为了让大家进一步加深对这两种解析器的理解。


 

4. SAX解析XML文件方式

 SAX解析XML文件主要分为四大步骤,接下来让我们一起看下大致的解析过程吧!

  1. 得到xml文件对应的资源,可以是xml的输入流,文件或uri
  2. 得到SAX解析工厂(SAXParserFactory)
  3. 由解析工厂生产一个SAX解析器(SAXParser)
  4. 传入输入流和handler给解析器,调用parse()方法解析
    @Test
    public void testSaxParser()throws Exception{
        //1.得到xml文件对应的资源,可以是xml的输入流,文件和uri
        Resource resource = new ClassPathResource("student.xml");
        InputStream inputStream = resource.getInputStream();

        //2.得到SAX解析工厂(SAXParserFactory)
        SAXParserFactory saxparserFactory = SAXParserFactory.newInstance();

        //3.由解析工厂生产一个SAX解析器(SAXParser)
        SAXParser saxparser = saxparserFactory.newSAXParser();

        //4.传入输入流和handler给解析器,调用parse()解析
        saxparser.parse(inputStream, new XmlParseHandler());

    }

1). student.xml文件代码清单

<root>
    <student id="1">
        <name>张三</name>
        <sex>男</sex>
        <age>18</age>
    </student>
    <student id="2">
        <name>李四</name>
        <sex>女</sex>
        <age>18</age>
    </student>
</root>

2). Student代码清单

public class Student {

    private int id;
    private String name;
    private String sex;
    private int 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 String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

3). XmlParseHandler.java代码清单

public class XmlParseHandler extends DefaultHandler {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 记录当前解析到的节点名称
     */
    private String currentTag;

    /**
     * 定义存储Student对象的集合
     */
    List<Student> listStu;

    /**
     * 定义student对象
     */
    private Student student;


    /**
     * 文档解析结束后调用
     */
    @Override
    public void endDocument() throws SAXException {
        listStu.forEach(e -> {
            logger.info(e.toString());
        });
        logger.info("文档解析结束...");
    }

    /**
     * 节点解析结束后调用
     *
     * @param uri       : 命名空间的uri
     * @param localName : 标签的名称
     * @param qName     : 带命名空间的标签名称
     */
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        logger.info("节点解析结束...");
        if (qName.equals("student")) {
            this.listStu.add(this.student);
        }
        this.currentTag = null;
    }

    /**
     * 文档解析开始调用
     */
    @Override
    public void startDocument() throws SAXException {
        logger.info("文档开始解析...");
        listStu = new ArrayList<>(10);
    }

    /**
     * 节点解析开始调用
     *
     * @param uri       : 命名空间的uri
     * @param localName : 标签的名称
     * @param qName     : 带命名空间的标签名称
     */
    @Override
    public void startElement(String uri, String localName, String qName,
                             Attributes attributes) throws SAXException {
        logger.info("节点开始解析...");
        if (qName.equals("student")) {
            student = new Student();
            student.setId(Integer.parseInt(attributes.getValue(0)));
        }
        // 把当前标签记录下来
        this.currentTag = qName;
    }

    /**
     * 获取标签元素内容
     * @param ch:标签元素内容
     * @param start:从第几个开始获取
     * @param length:字符长度
     * @throws SAXException
     */
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        logger.info("开始获取标签内容...");
        String tagName = this.currentTag;
        if (tagName != "" && tagName != null && tagName.length() > 0) {
            //date为解析后得到的数据
            String date = new String(ch, start, length);
            //给student对象赋值
            setStudent(tagName, date);
        }
    }

    /**
     * 给student对象赋值
     * @param tagName:标签名
     * @param date:解析后的数据
     */
    public void setStudent(String tagName, String date) {
        switch (tagName) {
            case "name":
                this.student.setName(date);
                break;
            case "sex":
                this.student.setSex(date);
                break;
            case "age":
                this.student.setAge(Integer.parseInt(date));
                break;
            default:
                break;
        }
    }

}

本篇的核心类是XmlParseHandler,继承了DefaultHandler,重写了里面的核心方法。到这里SAX解析XML文件的基本流程就已经结束了, 感谢大家的阅读和支持!!!