【Java】基础知识-JAXP解析XML(二)

763 阅读4分钟

我正在参与掘金创作者训练营第6期, 点击了解活动详情

1 概述

在上一个章节,简单的操作了下JAXP使用DOM解析XML,这一个章来简单说下Sax解析XML。在讲之前,我感觉多说一下什么我们为什么使用Sax来解析,使用原来的DOM解析器不好么?这里引入一个知识点——「CAP原则」。

简单来说,CAP原则又称CAP定理,指的是在一个分布式系统中,有三种属性原则,他们分别是

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分区容错性(Partition tolerance)。

首字母组合之后就是CAP原则,CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。有兴趣的可以查下,这里不在做过多的表述。

JAXP(Java AOI for XML Processing)是 SUN 公司推出的解析标准实现,这个工具提供了两种解析器方式来解析XML。其中一种就是本文所讲的「Sax解析器」。SAX解析器被称作SAXParserSAXParser是由javax.xml.parsers.SAXParserFactory创建的。与上一章节的DOM不同,Sax不需要在内存解释,所以速度上比较快,而且Sax是通过w3c执行标准org.xml.sax.helpers.DefaultHandler事件回调来实现查找元素。

2 JAXP-SAX解析

XML是一种通用的数据交换格式,它的平台无关,语言无关,系统无关,在不同的语言环境的解析方式都是一样的,只不过是实现的语法不同。

SAX解析方式会逐行地去扫描XML文档,当遇到标签时会触发解析处理器,采用事件处理的方式解析XML (Simple API for XML) ,不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。

SAX解析可分四个步骤进行:

1、实例化SAX解析工厂(SAXParserFactory),创建SAX解析器

SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();

image.png

2、由解析器解析得到xml文件对应的资源,可以是xml的输入流,文件和uri,从图中提供的API我们可以看出来需要提供的参数

saxParser.parse(new File(xmlPath), new CustomHandler());

image.png

其中自定义类CustomHandler()继承了DefaultHandler implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler实现。而我们在使用过程中,实现最多的要数ContentHandler这个类中的实现。主要涉及到的方法有

  • characters (char ch[], int start, int length)

  • startDocument ()

  • endDocument()

  • startElement (String uri, String localName, String qName, Attributes atts)

  • endElement (String uri, String localName, String qName)

3、由用户自定义实现来处理XML

Sax解析的流程图如下:

image.png

2.1 创建实验类SaxParserXmlTest,创建xml文件

首先创建一个类SaxParserXmlTest.java,然后创建一个persons.xml,xml可以使用上一章节我们用过的。

    public static void main(String[] args) {
        
        String xmlPath = "./tool-jdk8/src/main/java/com/liuyc/tooljdk/xml/persons.xml";

        try {
            // 1、创建解析器工厂,创建解析器
            SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();

            // 2、执行解析转换操作
            saxParser.parse(new File(xmlPath), new CustomHandler());

        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

2.2 编写CustomHandler继承DefaultHandler

该类是SAX解析的核心所在,我们要重写以下几个我们关心的方法。

1、startDocument () :文档解析开始时调用,该方法只会调用一次(可以省略)

2、startElement (String uri, String localName, String qName, Attributes attributes)

3、characters (char[] ch, int start, int length)

4、endElement (String uri, String localName, String qName)

5、endDocument () :文档解析结束后调用,该方法只会调用一次(可以省略)

class CustomHandler extends DefaultHandler {
    /**
     * 元素个数
     */
    public int sumElement = 0;

    /**
     * 元素个数【person】
     */
    public int sumPerson = 0;

    @Override
    public void startDocument() throws SAXException {
        System.out.println("开始解析文档==>");
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        // 拼接标签的属性
        if (attributes != null && attributes.getLength() > 0) {
            String attrs = "";
            for (int i = 0; i < attributes.getLength(); i++) {
                String attrName = attributes.getQName(i);
                String value = attributes.getValue(i);
                attrs += " " + attrName + "="" + value + """;
            }
            System.out.print("<" + qName + attrs + ">");
        }
        // 标签没有属性,直接输出
        else {
            System.out.print("<" + qName + ">");
        }

        if("person".equals(qName)) {
            sumPerson ++;
        }
        sumElement ++;
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        System.out.print("</" + qName + ">");
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        System.out.print(new String(ch, start, length));
    }

    @Override
    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        System.out.println("[prefix]" + prefix + "[uri]" + uri);
    }

    @Override
    public void endDocument() throws SAXException {
        System.out.println();
        System.out.println("完成解析文档==>");
        System.out.println("共有" + sumElement + "个元素,其中【person】元素有" + sumPerson + "个");
    }

}

上面参数属性

  • uri : 名称空间 URI ,如果元素没有则为空字符串。
  • localName : 本地名称,如果没有名称空间,则为空字符串。
  • qName : 限定的名称,可以理解为就是标签,例如<person></person>person就是qName,当然这里没有值。
  • attributes: 附加到元素的属性,可以理解为标签上的属性对,有名称(qName)和值(value),例如uid="QS#123"uid就是qNameQS#123就是value
  • characters :文本

2.3 解析结果

sax方式解析XML文档结束,打印如下结果。

开始解析文档==>
<persons>
	<person sid="001" uid="QS#123">
		<name>张小帅</name>
		<sex></sex>
		<age>28</age>
	</person>
	<person sid="002">
		<name>刘晓萌</name>
		<sex></sex>
		<age>21</age>
	</person>
	<person sid="003">
		<name>王老四</name>
		<sex></sex>
		<age>38</age>
	</person>
</persons>
完成解析文档==>
共有13个元素,其中【person】元素有3

可以看出来,SAX是一行一行处理。

2.4 总结

SAX解析XML具有解析速度快,占用内存少,对于Android等移动设备来说有巨大的优势,深入了解SAX的事件触发机制是掌握SAX解析的关键,掌握了SAX的事件触发就掌握了SAX解析XML。