XML系列(下篇)——Java和XML的这些事你可能真的不知道

前言

我们在XML系列上篇中介绍了XML的入门使用,本篇文章将围绕dom、sax、dom4j、jaxp、jaxb等众多概念来进行关系梳理,叙述Java在解析XML过程中经历了哪些阶段,希望能够让对上述概念不清晰的读者有所帮助。

如果你还没阅读过本系列的上篇,可以从下面的链接跳转阅读:
XML系列(上篇)——XML文件入门详解


先说说DOM和SAX

DOMSAX都是对于解析XML提出的标准,是jdomdom4j等实际应用的存在前提和基石。

1)DOM

DOM 是 W3C 处理 XML 的标准 API,它是许多其它与 XML 处理相关的标准的基础,不仅是 Java,其它诸如 JavaScript,PHP,MS .NET 等等语言都实现了该标准。DOM对于XML文件采用的解析方式是一次性加载整个XML文档,在内存中形成一个树形的数据结构,这个数据结构我们称为文档对象模型。通过DOM解析器获取Documen文档t对象后,开发人员可以利用getDocumentElement();(获取文档元素)、getFirstChild()(获取第一个子元素)等API来便捷地操作文档树。但DOM的缺点也很明显,如果XML文档很大就可能造成内存溢出的风险。

2)SAX

SAX是Simple API for XML的缩写,需要注意的是,它并不是由W3C官方所提出的标准,可以说是“民间”的事实标准。实际上,它是一种社区性质的讨论产物,同时SAX也是在JAVA平台上第一个被广泛使用的XML API。虽然如此,在XML中对SAX的应用丝毫不比DOM少,几乎所有的XML解析器都会支持它。在解析方式上,SAX是SAX是基于事件解析,不需要等到整个XML文件被加载完成后在开始处理,而是加载到哪处理到哪。这样便带来了效率上的优势。SAX处理的优点非常类似于流媒体。分析能够立即开始,而无需等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。适用于大型文档。但SAX的缺点也很明显:不能随机访问节点,比如我们读取到文档第二个元素时需要回过头读取第一个元素,就需要再扫描一次了。

接着说 JAXP

JAXP,全称Java API for XML Processing,打开其为JDK的目录:javax.xml.parsers, 你会发现它与SAX和DOM一样只是一套API。实际上,JAXP出现时SUN公司为了弥补JAVA在XML标准制定上的空白而制定的一套JAVA XML标准API。它并没有为JAVA解析XML提供任何新功能,但是它为在JAVA获取SAX与DOM解析器提供了更加直接的途径。它封装了sax\dom两种接口,并在sax\dom的基础之上,作了一套比较简单的api以供开发。事实上,我们可以把JAXP看做是sun公司对SAX和DOM二者结合后产生的相对来说更加解耦的一套API。底层实现解析的方式还是DOM和SAX

我们可以将三者的区别总结成下面的表格:


DOM、SAX、JAXP三者对比

虽然上面已经介绍了DOM、SAX和JAXP三套接口, 但是有了接口后我们并不能马上使用,具体的实现还需要具体的实现类来完成。在java中,上述的接口都是通过xerces解释器来实现的,具体在om.sun.org.apache.xerces.internal包。xerces被称为性能最好的解释器,除了xerces外,还有其他的第三方解释器,如crimson。
下面我们来通过一个案例来演示java是如何通过以上三种方式解析XML文档的:

先看xml演示文件:


    
        
            李连杰
            25
        
        
            李小龙
            65
        
    
    
        
             成龙< 
            14
        
        
            小明
            12
        
    

xerces解释器下提供了DOM解析的具体实现类,假设我们现在想要读取所有教师的信息,相关的代码如下:
@Test
    public void readXMLFile() {
        DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder domBuilder = domfac.newDocumentBuilder();
            File file = new File("xml文件的路径");
            InputStream is = new FileInputStream(file);
            org.w3c.dom.Document doc = domBuilder.parse(is);
            NodeList teacherList = doc.getElementsByTagName("teacher"); //获取cssItem节点
            // 定义一个List集合,承接读取结果
            List resultList = new ArrayList();
            if (teacherList != null) {
                for(int i=0 ; i < teacherList.getLength() ; i++) {
                    NodeList childNodes = teacherList.item(i).getChildNodes();
                    for(int j=0; j < childNodes.getLength(); j++){
                        if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
                            resultList.add(childNodes.item(j).getTextContent()); // 将结果封装到List集合中
                        }
                    }
                }
                System.out.println(resultList.toString());
            }
        } catch (ParserConfigurationException e) {
            System.out.println(e);
        } catch (FileNotFoundException e) {
            System.out.println(e);
        } catch (SAXException e) {
            System.out.println(e);
        } catch (IOException e) {
            System.out.println(e);
        }
    }
使用SAX方式解析,java代码如下:
    @Test
    public void readXMLBySAX() throws Exception {
        DefaultHandler handler = new MyDefaultHandler();
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        xmlReader.setContentHandler(handler);
        xmlReader.parse(new InputSource("xml文档路径"));
    }

    class MyDefaultHandler extends DefaultHandler{
        private StringBuffer buf;
        private String str;
        public MyDefaultHandler(){
            super();
        }

        public void startDocument() throws SAXException{
            buf=new StringBuffer();
            System.out.println("*******开始解析文档*******");
        }

        public void endDocument() throws SAXException{
            System.out.println("*******文档解析结束*******");
        }

        public void startPrefixMapping( String prefix, String uri ){
            System.out.println(" 前缀映射: " + prefix +" 开始!"+ " 它的URI是:" + uri);
        }

        public void endPrefixMapping( String prefix ){
            System.out.println(" 前缀映射: "+prefix+" 结束!");
        }

        public void startElement(String namespaceURI,String localName,String qName,Attributes atts){
            System.out.println("*******开始解析元素*******");
            System.out.println("元素名"+qName);
            for(int i=0;i
使用JAXP的DOM相关API:
/**
     * 由于JAXP封装了DOM和SAX接口,所以两种解析方式都可以。这里的话只举例DOM
     */
    @Test
    public void readXMLByJaxp() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.parse("xml路径");
        NodeList teacherList = document.getElementsByTagName("teacher");
        List resultList = new ArrayList();
        if (teacherList != null) {
            for(int i=0 ; i < teacherList.getLength() ; i++) {
                NodeList childNodes = teacherList.item(i).getChildNodes();
                for(int j=0; j < childNodes.getLength(); j++){
                    if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
                        resultList.add(childNodes.item(j).getTextContent()); // 将结果封装到List集合中
                    }
                }
            }
            System.out.println(resultList.toString());
        }
    }

我们可以发现使用JAXP解析XML文档和分别使用其他两种方式解析是差不多的。同时,解释器解析文档的步骤还是略显麻烦,尤其是SAX解析,特别麻烦。由于Java原生的API使用起来并不方便,所以也就引起了后面更加灵活、更加简便的JDOM和DOM4J出现。

1)JDOM

JDOM是专门为java提出的,使用效率比普通DOM更快。由于是第一个 Java 特定模型,JDOM 一直得到大力推广和促进。正在考虑通过“Java 规范请求 JSR-102”将它最终用作“Java 标准扩展”。从 2000 年初就已经开始了 JDOM 开发。JDOM 与 DOM 主要有两方面不同。1、JDOM 仅使用具体类而不使用接口 2、JDOM中大量使用了Collections类,简化了开发人员的上手难度。

2)DOM4J

DOM4J 代表了完全独立的开发结果,但最初,它是 JDOM 的一种智能分支。它合并了许多超出基本 XML 文档表示的功能,包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过 DOM4J API 和标准 DOM 接口具有并行访问功能。从 2000 下半年开始,它就一直处于开发之中。DOM4J有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的 Java 软件都在使用 DOM4J 来读写XML,特别值得一提的是连SunJAXM也在用DOM4J
目前来说,这两种手段都有项目还在使用,但DOM4J已经成为了各种框架和项目使用的主流,例如大名鼎鼎的 hibernate也用 DOM4J 来读取 XML 配置文件。下面我们就用DOM4J来做一个解析小案例吧。
解析并输出xml文档的老师姓名和年龄(xml文件和之前的用例一致)

    @Test
    public void parseXmlByDom4j() throws DocumentException {
        // 1、 创建SAXReader对象
        SAXReader reader = new SAXReader();
        // 2、指定要解析的XML文件
        Document document = reader.read("xml的路径").getDocument();
        // 3、 遍历所有teacer节点下的内容
         Element rootElement = document.getRootElement();
            for (Element element : rootElement.element("teachers").elements()) {
                String qname = element.getQName().getName();
                String teacherName = element.element("name").getText();
                String teacherAge = element.element("age").getText();
                System.out.println(qname + ": name = " + teacherName + " age = " + teacherAge);
            }
    }
最后说说JAXB

JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。
简单来说就是,JAXB为我们提供了这样一种技术:可以通过schema直接将xml文档转为JAVA对象。这种技术是在JDK1.6之后开始应用起来的,在一些业务场景下使用会更加方便。

小结

本篇文章从解析XML的两种方式DOM和SAX开始讲起,引出了Sun公司为了补充自身在xml解析上的空白而推出的JAXP解析(只是将DOM和SAX进行简单封装),又因为JAXP本身并没有解决API本身的复杂性,所以后面市场上就又出现了JDOM和DOM4J解析,他们专门为JAVA设计,简便实用,其中又以功能强大的DOM4J成为了市场使用的主流。同时在JDK1.6之后,java也推出了JAXB简化了一些场景下对XML解析的使用。
至此,本篇文章对于java解析XML的历史已经介绍完毕,希望对各位读者有所收获。对于JAXM、JAX-RPC等概念由于日常工作中我们很少会接触到,就不在本篇文章中介绍了。同时,本篇文章也参考了多篇文章的内容讲解,大家也可以从参考资料中再去看其他更加详细的内容。

参考资料
Java for XML: JAXP、JAXB、JAXM、JAX-RPC、JAX-WS:
https://blog.csdn.net/hotdust/article/details/52016804
DOM、JDOM、DOM4J的区别:
https://www.cnblogs.com/avivahe/p/5493060.html
关于SAX,DOM,JAXP,JDOM,DOM4J的一些理解:
https://blog.csdn.net/robertleejesus/article/details/2624434
dom4j的使用和分析(重点对比和DOM、SAX的区别):
https://zhuanlan.zhihu.com/p/106094171
用DefaultHandler解析XML的Demo:
https://blog.csdn.net/feishin/article/details/8610272

你可能感兴趣的:(XML系列(下篇)——Java和XML的这些事你可能真的不知道)