> 文档中心 > 【Java基础系列教程】第二十二章 Java XML解析

【Java基础系列教程】第二十二章 Java XML解析


一、XML概述

1.1 XML简介

    可扩展标记语言,标准通用标记语言的子集,简称XML。是一种用于标记电子文件使其具有结构性的标记语言。
    
    在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。是Internet环境中跨平台的、依赖于内容的技术,也是当今处理分布式结构信息的有效工具。

    早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。

    可扩展标记语言与Access、Oracle和SQL Server等数据库不同,数据库提供了更强有力的数据存储和分析能力,例如:数据索引、排序、查找、相关一致性等,可扩展标记语言仅仅是存储数据。事实上它与其他数据表现形式最大的不同是:可扩展标记语言极其简单,这是一个看上去有点琐细的优点,但正是这点使它与众不同。
    
    XML的简单易于在任何应用程序中读/写数据,这使XML很快成为数据交换的唯一公共语言,虽然不同的应用软件也支持其他的数据交换格式,但不久之后它们都将支持XML,那就意味着程序可以更容易的与Windows、Mac OS、Linux以及其他平台下产生的信息结合,然后可以很容易加载XML数据到程序中并分析它,并以XML格式输出结果。

1.2 XML发展历史

    XML有两个先驱:SGML和HTML,这两个语言都是非常成功的标记语言,但是都有一些与生俱来的缺陷。XML正是为了解决它们的不足而诞生的。

    SGML:
        早在Web未发明之前,SGML(Standard Generalized Markup Language,标准通用标记语言)就已存在,正如它的名称所言,SGML是国际上定义电子文件结构和内容描述的标准。SGML具有非常复杂的文档结构,主要用于大量高度结构化数据的访问和其他各种工业领域,在分类和索引数据中非常有用。

        虽然SGML的功能很强大,但是它不适用于Web数据描述,而且SGML软件的价格非常昂贵;另外,SGML十分庞大,既不容易学,又不容易使用,在计算机上实现也十分困难;不仅如此,几个主要的浏览器厂商都明确拒绝支持SGML,这无疑是SGML在网上传播遇到的最大障碍。鉴于这些因素,Web的发明者——欧洲核子物理研究中心的研究人员,根据当时(1989年)的计算机技术,发明并推出了HTML。

    HTML:
        1989年,HTML诞生,它抛弃了SGML复杂庞大的缺点,继承了SGML的很多优点。HTML最大的特点是简单性和跨平台性。
        HTML是一种界面技术,它只使用了SGML中很少的一部分标记,例如HTML 4.0中只定义了70余种标记。为了便于在计算机上实现,HTML规定的标记是固定的,即HTML语法是不可扩展的。HTML这种固定的语法使它易学易用,在计算机上开发HTML的浏览器也十分容易。正是由于HTML的简单性,使得基于HTML的Web应用得到了极大的发展。

    XML的产生:
        随着Web应用的不断发展,HTML的局限性也越来越明显地显现了出来,如HTML无法描述数据、可读性差、搜索时间长等。人们又把目光转向SGML,再次改造SGML使之适应现在的网络需求。随着先辈的努力,1998年2月10日,W3C(World Wide Web Consortium,万维网联盟)公布XML 1.0标准,XML诞生了。

        XML最初的设计目的是为了EDI(Electronic Data Interchange,电子数据交换),确切地说是为EDI提供一个标准数据格式。

        当前的一些网站内容建设者们已经开始开发各种各样的XML扩展,比如数学标记语言MathML、化学标记语言CML等。此外,一些著名的IT公司,如Oracle、IBM以及微软等都积极地投入人力与财力研发XML相关软件与服务支持,这无疑确定了XML在IT产业的重要地位。

1.3 XML特征

    (1) XML可以从HTML中分离数据。即能够在HTML文件之外将数据存储在XML文档中,这样可以使开发者集中精力使用HTML做好数据的显示和布局,并确保数据改动时不会导致HTML文件也需要改动,从而方便维护页面。XML也能够将数据以“数据岛”的形式存储在HTML页面中,开发者依然可以把精力集中到使用HTML格式化和显示数据上。
        数据岛:https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%B2%9B/2897494?fr=aladdin
        我们之前用过ajax请求json-server里面的数据;其实我们如果不学习json-server,还可以通过xml来存储数据,然后通过js来获取xml里面的数据;
        通过js解析xml数据:https://www.w3school.com.cn/xml/index.asp

    (2) XML可用于交换数据。基于XML可以在不兼容的系统之间交换数据,计算机系统和数据库系统所存储的数据有多种形式,对于开发者来说,最耗时间的工作就是在遍布网络的系统之间交换数据。把数据转换为XML格式存储将大大减少交换数据时的复杂性,还可以使这些数据能被不同的程序读取。

    (3) XML可应用于B2B中。例如在网络中交换金融信息,目前XML正成为遍布网络的商业系统之间交换信息所使用的主要语言,许多与B2B有关的完全基于XML的应用程序正在开发中。

    (4) 利用XML可以共享数据。XML数据以纯文本格式存储,这使得XML更易读、更便于记录、更便于调试,使不同系统、不同程序之间的数据共享变得更加简单。

    (5) XML可以充分利用数据。XML是与软件、硬件和应用程序无关的,数据可以被更多的用户、设备所利用,而不仅仅限于基于HTML标准的浏览器。其他客户端和应用程序可以把XML文档作为数据源来处理,就像操作数据库一样,XML的数据可以被各种各样的“阅读器”处理。

    (6) XML可以用于创建新的语言。比如,WAP和WML语言都是由XML发展来的。WML(Wireless Markup Language,无线标记语言)是用于标识运行于手持设备上(比如手机)的Intemet程序的工具,它就采用了XML的标准。

    总之,XML使用一个简单而又灵活的标准格式,为基于Web的应用提供了一个描述数据和交换数据的有效手段。但是,XML并非是用来取代HTML的。HTML着重如何描述将文件显示在浏览器中,而XML与SGML相近,它着重描述如何将数据以结构化方式表示。

1.4 XML与HTML区别

    (1) 可扩展性方面:HTML不允许用户自行定义他们自己的标识或属性(其实HTML5已经运行自定义标签和属性),而在XML中,用户能够根据需要自行定义新的标识及属性名,以便更好地从语义上修饰数据。

    (2) 结构性方面:HTML不支持深层的结构描述,XML的文件结构嵌套可以复杂到任意程度,能表示面向对象的等级层次。

    (3) 可校验性方面:HTML没有提供规范文件以支持应用软件对HTML文件进行结构校验,而XML文件可以包括一个语法描述,使应用程序可以对此文件进行结构校验。

二、XML语法

    XML文件格式是纯文本格式,在许多方面类似于HTML,XML由XML元素组成,每个XML元素包括一个开始标记、一个结束标记以及两个标记之间的内容,例如,可以将XML元素标记为价格、订单编号或名称。标记是对文档存储格式和逻辑结构的描述。在形式上,标记中可能包括注释、引用、字符数据段、起始标记、结束标记、空元素、文档类型声明( DTD)和序言。

具体规则如下:

    1、必须有声明语句,XML声明是XML文档的第一句,其格式如下:
       

    2、注意大小写:
        在XML文档中,大小写是有区别的。“

”和“

”是不同的标记。注意在写元素时,前后标记的大小写要保持一致。最好养成一种习惯,或者全部大写,或者全部小写,或者大写第一个字母,这样可以减少因为大小写不匹配而产生的文档错误。

    3、XML文档有且只有一个根元素
        良好格式的XML文档必须有一个根元素,就是紧接着声明后面建立的第一个元素,其他元素都是这个根元素的子元素,根元素完全包括文档中其他所有的元素。根元素的起始标记要放在所有其他元素的起始标记之前;根元素的结束标记要放在所有其他元素的结束标记之后。

    4、属性值使用引号
        在HTML代码里面,属性值可以加引号,也可以不加。但是XML规定,所有属性值必须加引号(可以是单引号,也可以是双引号,建议使用双引号),否则将被视为错误。

    5、所有的标记必须有相应的结束标记
        在HTML中,标记可以不成对出现,而在XML中,所有标记必须成对出现,有一个开始标记,就必须有一个结束标记,否则将被视为错误。

    6、所有的空标记也必须被关闭,空标记是指标记对之间没有内容的标记,比如“”等标记。在XML中,规定所有的标记必须有结束标记。

        白晶晶  28           至尊宝  300      

三、XML的四种解析方式

    XML是一种通用的数据交换格式,它的平台无关性、语言无关性、系统无关性、给数据集成与交互带来了极大的方便。XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已。

    XML的解析方式分为四种:
        1、DOM解析;
        2、SAX解析;
        3、JDOM解析;
        4、DOM4J解析。

    其中前两种属于基础方法,是官方提供的平台无关的解析方式;后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于Java平台。

针对以下XML文件,会对四种方式进行详细描述:

     冰与火之歌 乔治马丁 2014 89         安徒生童话 2004 77 English        

3.1 DOM解析

    DOM的全称是Document Object Model,也即文档对象模型。在应用程序中,基于DOM的XML分析器将一个XML文档转换成一个对象模型的集合(通常称DOM树),应用程序正是通过对这个对象模型的操作,来实现对XML文档数据的操作。通过DOM接口,应用程序可以在任何时候访问XML文档中的任何一部分数据,因此,这种利用DOM接口的机制也被称作随机访问机制。

    DOM接口提供了一种通过分层对象模型来访问XML文档信息的方式,这些分层对象模型依据XML的文档结构形成了一棵节点树。无论XML文档中所描述的是什么类型的信息,即便是制表数据、项目列表或一个文档,利用DOM所生成的模型都是节点树的形式。也就是说,DOM强制使用树模型来访问XML文档中的信息。由于XML本质上就是一种分层结构,所以这种描述方法是相当有效的。

    DOM树所提供的随机访问方式给应用程序的开发带来了很大的灵活性,它可以任意地控制整个XML文档中的内容。然而,由于DOM分析器把整个XML文档转化成DOM树放在了内存中,因此,当文档比较大或者结构比较复杂时,对内存的需求就比较高。而且,对于结构复杂的树的遍历也是一项耗时的操作。所以,DOM分析器对机器性能的要求比较高,实现效率不十分理想。不过,由于DOM分析器所采用的树结构的思想与XML文档的结构相吻合,同时鉴于随机访问所带来的方便,因此,DOM分析器还是有很广泛的使用价值的。

优点:

    1、形成了树结构,有助于更好的理解、掌握,且代码容易编写。

    2、解析过程中,树结构保存在内存中,方便修改。

缺点:

    1、由于文件是一次性读取,所以对内存的耗费比较大。

    2、如果XML文件比较大,容易影响解析性能且可能会造成内存溢出。

操作步骤:

    1、创建 DocumentBuilderFactory,由 newInstance() 方法获取工厂实例;

    2、由 DocumentBuilderFactory 工厂创建 DocumentBuilder,由 newDocumentBuilder() 创建DocumentBuilder;

    3、通过 Document dom = builder.parse(file|String);读取xml文档创建dom对象;

    4、通过dom对象的一系列方法获取某个结点、某名字的结点列表;
        Element getElementById(String elementId)    返回具有带给定值的 ID 属性的 Element。 
         NodeList getElementsByTagName(String tagname)    按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList。
         
     5、通过NodeList对象获取结点列表中某个结点;
         Node item(int index)    返回集合中的第 index 个项。 
     
     6、通过Node对象读取结点属性;
         NamedNodeMap getAttributes()    包含此节点的属性的 NamedNodeMap(如果它是 Element);否则为 null。
         然后对NamedNodeMap对象调用以下方法获取属性(前面我们说过,属性也是结点):
             Node  getNamedItem(String name)    检索通过名称指定的节点。
              或
            Node item(int index)    返回映射中第 index 个项。
        最后,对返回的属性通过以下方法获取名、值(属性也是结点):
            String getNodeName()     此节点的名称,取决于其类型; 
             String getNodeValue()     此节点的值,取决于其类型;
             
    7、获取结点的子节点列表;
         NodeList getChildNodes()    包含此节点的所有子节点的 NodeList。 
     
     8、获取结点值;
         String getNodeName()    此节点的名称,取决于其类型; 
         String getNodeValue()    此节点的值,取决于其类型;
         String getTextContent()    获取此节点的文本内容;

import java.io.IOException;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import org.w3c.dom.Document;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.xml.sax.SAXException;public class DOMTest {    public static void main(String[] args) { // 创建一个DocumentBuilderFactory的对象 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try {     // 创建DocumentBuilder对象     DocumentBuilder db = dbf.newDocumentBuilder();     // 通过DocumentBuilder对象的parser方法加载books.xml文件到当前项目下     Document document = db.parse("src/books.xml");     // 获取所有book节点的集合     NodeList bookList = document.getElementsByTagName("book");     // 通过nodelist的getLength()方法可以获取bookList的长度     System.out.println("一共有" + bookList.getLength() + "本书");     // 遍历每一个book节点     for (int i = 0; i < bookList.getLength(); i++) {  System.out.println("=================下面开始遍历第" + (i + 1) + "本书的内容=================");  // 通过 item(i)方法 获取一个book节点,nodelist的索引值从0开始  Node book = bookList.item(i);  // 获取book节点的所有属性集合  NamedNodeMap attrs = book.getAttributes();  System.out.println("第 " + (i + 1) + "本书共有" + attrs.getLength() + "个属性");  // 遍历book的属性  for (int j = 0; j < attrs.getLength(); j++) {      // 通过item(index)方法获取book节点的某一个属性      Node attr = attrs.item(j);      // 获取属性名      System.out.print("属性名:" + attr.getNodeName());      // 获取属性值      System.out.println("--属性值" + attr.getNodeValue());  }  // 解析book节点的子节点  NodeList childNodes = book.getChildNodes();  // 遍历childNodes获取每个节点的节点名和节点值  System.out.println("第" + (i + 1) + "本书共有" + childNodes.getLength() + "个子节点");  for (int k = 0; k < childNodes.getLength(); k++) {      // 区分出text类型的node以及element类型的node      if (childNodes.item(k).getNodeType() == Node.ELEMENT_NODE) {   // 获取了element类型节点的节点名   System.out.print("第" + (k + 1) + "个节点的节点名:" + childNodes.item(k).getNodeName());   // 获取了element类型节点的节点值   System.out.println("--节点值是:" + childNodes.item(k).getFirstChild().getNodeValue());   // System.out.println("--节点值是:" + childNodes.item(k).getTextContent());      }  }  System.out.println("======================结束遍历第" + (i + 1) + "本书的内容=================");     } } catch (ParserConfigurationException e) {     e.printStackTrace(); } catch (SAXException e) {     e.printStackTrace(); } catch (IOException e) {     e.printStackTrace(); }    }}

    总结:DOM方法解析XML文档,把元素、元素属性、元素值都看作Node类型,通过node.getNodeName()获取元素名、属性名,通过getNodeValue()获取属性值、元素值,通过getChildNodes()获取子节点们,通过item(i)获取第i个属性或者第i个子节点。
    
    这个所谓的节点,我们要参照之前学习的JS,因为有属性节点、文本节点、注释节点、空节点、元素节点。

3.2 SAX解析

    SAX的全称是Simple APIs for XML,也即XML简单应用程序接口。与DOM不同,SAX提供的访问模式是一种顺序模式,这是一种快速读写XML数据的方式。当使用SAX分析器对XML文档进行分析时,会触发一系列事件,并激活相应的事件处理函数,应用程序通过这些事件处理函数实现对XML文档的访问,因而SAX接口也被称作事件驱动接口;        

优点:

    1、采用事件驱动模式,对内存耗费比较小。

    2、适用于只处理XML文件中的数据时。

缺点:

    1、编码比较麻烦。

    2、很难同时访问XML文件中的多处不同数据。

操作步骤:

    1、创建解析工厂:SAXParserFactory factory = SAXParserFactory.newInstance();

    2、由工厂创建解析器:SAXParser parser = factory.newSAXParser();

    3、通过解析器的parse()方法,对指定xml文档以指定handler之类进行解析查询parser.parse(xmlFile, new MySaxHandler());我们要继承DefaultHandler类,定义相应的查询操作类;
        3.1 重写父类中的文档开始方法、文档结束方法,定义开始、结束遍历xml文档时的操作;
            void startDocument()    接收文档开始的通知。 
            void endDocument()    接收文档结束的通知。 

        3.2 重写父类的标签开始方法、标签结束方法,定义遍历到一个开始、结束标签时的操作;
            //参数qName是标签名、attributes是属性列表    
            void startElement(String uri, String localName, String qName, Attributes attributes) 接收元素开始的通知。 
            void endElement(String uri, String localName, String qName) 接收元素结束的通知。

        3.3 重写characters(char[] ch, int start, int length)方法;
            // 对事件发生时,元素的字符怎么处理  
            public void characters(char[] ch, int start, int length) throws SAXException             {
            //参数ch是当上述4种事件随便一个发生时,对应的元素的值,值在ch中start开始,length长。从头到尾遍历整个xml文档时,每个标签的值依次被存入ch中。
             }
         也就是说,通过SAX解析xml文档是没有dom对象出现的,所以不会有node,不会有getNodeName()、getNodeValue()获取结点名、值。    

SAXTest代码:

import java.io.IOException;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import org.xml.sax.SAXException;public class SAXTest {    /**     * @param args     */    public static void main(String[] args){ //由SAXParserFactory创建解析工厂 SAXParserFactory factory = SAXParserFactory.newInstance();  //由工厂创建解析器 try {     SAXParser parser = factory.newSAXParser();   //对指定xml文档以指定handler类     SAXParserHandler handler = new SAXParserHandler();     parser.parse("src/books.xml", handler);     System.out.println("共有" + handler.getBookList().size() + "本书");   for (Book book : handler.getBookList()) {  System.out.println(book.getId());  System.out.println(book.getName());  System.out.println(book.getAuthor());  System.out.println(book.getYear());  System.out.println(book.getPrice());  System.out.println(book.getLanguage());  System.out.println("----finish----");     } } catch (ParserConfigurationException e) {     // TODO Auto-generated catch block     e.printStackTrace(); } catch (SAXException e) {     // TODO Auto-generated catch block     e.printStackTrace(); } catch (IOException e) {     // TODO Auto-generated catch block     e.printStackTrace(); }    }}

SAXParserHandler代码:

import java.util.ArrayList;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;public class SAXParserHandler extends DefaultHandler {    String value = null;    Book book = null;    private ArrayList bookList = new ArrayList(); public ArrayList getBookList() { return bookList;    }    int bookIndex = 0;    /**     * 用来标识解析开始     */    @Override    public void startDocument() throws SAXException { // TODO Auto-generated method stub super.startDocument(); System.out.println("SAX解析开始");    } /**     * 用来标识解析结束     */    @Override    public void endDocument() throws SAXException { // TODO Auto-generated method stub super.endDocument(); System.out.println("SAX解析结束");    } /**     * 解析xml元素     */    @Override    public void startElement(String uri, String localName, String qName,     Attributes attributes) throws SAXException { //调用DefaultHandler类的startElement方法 super.startElement(uri, localName, qName, attributes); if (qName.equals("book")) {     bookIndex++;     //创建一个book对象     book = new Book();     //开始解析book元素的属性     System.out.println("======================开始遍历某一本书的内容=================");     //不知道book元素下属性的名称以及个数,如何获取属性名以及属性值     int num = attributes.getLength();     for(int i = 0; i < num; i++){  System.out.print("book元素的第" + (i + 1) +  "个属性名是:"   + attributes.getQName(i));  System.out.println("---属性值是:" + attributes.getValue(i));  if (attributes.getQName(i).equals("id")) {      book.setId(attributes.getValue(i));  }     } }else if (!qName.equals("bookstore")) {     System.out.print("节点名是:" + qName + "---"); }    } @Override    public void endElement(String uri, String localName, String qName)     throws SAXException { //调用DefaultHandler类的endElement方法 super.endElement(uri, localName, qName); //判断是否针对一本书已经遍历结束 if (qName.equals("book")) {     bookList.add(book);     book = null;     System.out.println("======================结束遍历某一本书的内容================="); }else if (qName.equals("name")) {     book.setName(value); }else if (qName.equals("author")) {     book.setAuthor(value); }else if (qName.equals("year")) {     book.setYear(value); }else if (qName.equals("price")) {     book.setPrice(value); }else if (qName.equals("language")) {     book.setLanguage(value); }    } @Override    public void characters(char[] ch, int start, int length)     throws SAXException { // TODO Auto-generated method stub super.characters(ch, start, length); value = new String(ch, start, length); if (!value.trim().equals("")) {     System.out.println("节点值是:" + value); }    }}

Book代码:

public class Book {    private String id;    private String name;    private String author;    private String year;    private String price;    private String language;    public Book() {    }    public Book(String id, String name, String author, String year, String price, String language) { super(); this.id = id; this.name = name; this.author = author; this.year = year; this.price = price; this.language = language;    }    public String getId() { return id;    }    public void setId(String id) { this.id = id;    }    public String getName() { return name;    }    public void setName(String name) { this.name = name;    }    public String getAuthor() { return author;    }    public void setAuthor(String author) { this.author = author;    }    public String getYear() { return year;    }    public void setYear(String year) { this.year = year;    }    public String getPrice() { return price;    }    public void setPrice(String price) { this.price = price;    }    public String getLanguage() { return language;    }    public void setLanguage(String language) { this.language = language;    }}

    总结:SAX解析XML文档的节点名是通过事件函数的参数qName获取的,属性是通过参数attributes的getValue("属性名")获取的,节点值是通过当前事件函数发生时,characters(char[] ch, int start, int length)方法中的内容获取的。

3.3 JDOM解析

    JDOM方法是根据DOM方法的众多繁琐操作进行包装得到的,上面我们看到,DOM方法解析XML文档其实是很繁琐的,而且很混乱,标签、属性、换行空格都当作结点类型来处理。JDOM方法定义了一系列通俗、好记的方法来解析XML,方法的底层封装了一系列DOM操作,但是我们不必亲自去进行这些繁琐的工作了。

优点:

    1、查找方便,可以修改。

    2、仅使用具体类,而不使用接口。

缺点:

    1、装载整个文档,对内存容量要求。
    
    2、API大量使用了Collections类。
        
    在JDOM中,同一了根节点、普通结点、属性等全为Element类型。
    
    在软件领域,JAR文件(Java归档,英语:Java Archive)是一种软件包文件格式,通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便开发Java平台应用软件或库。
    
    怎么导入jar包:
        在不同的学习阶段,jar的位置是不一样的;我们现在只需要知道在java基础阶段怎么引入jar包即可,把jar包放在根目录下面,然后jar包右键build path --> add to build path。
        
        在你需要导入的Jar包上,点击右键,选择Add as Library…

操作步骤:

    1、创建一个SAXbuilder:SAXBuilder builder = new SAXBuilder();  

    2、创建文件输入流打开xml文件:InputStream in = new FileInputStream("XXX.xml");  

    3、通过builder,从输入流读取xml文件创建dom对象:Document dom = builder.build(in);  

    4、获取根节点:Element root = dom.getRootElement();

    5、获取子节点列表、List childNodes = node.getChildren();

    6、遍历子节点列表,获取第i个结点:Element node = childNodes.get(i);

    7、读取结点信息:
        1)结点属性值:node.getAttributeValue("属性名");
        2)结点名:node.getName();
        3)结点值:node.getValue();
        4)子结点文本值:node.getChildText("子结点名");

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import org.jdom2.Attribute;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.JDOMException;import org.jdom2.input.SAXBuilder;public class JDOMTest {private static ArrayList booksList = new ArrayList();/** * @param args */public static void main(String[] args) {// 进行对books.xml文件的JDOM解析// 准备工作// 1.创建一个SAXBuilder的对象SAXBuilder saxBuilder = new SAXBuilder();InputStream in;try {// 2.创建一个输入流,将xml文件加载到输入流中in = new FileInputStream("src/books.xml");InputStreamReader isr = new InputStreamReader(in, "UTF-8");// 3.通过saxBuilder的build方法,将输入流加载到saxBuilder中Document document = saxBuilder.build(isr);// 4.通过document对象获取xml文件的根节点Element rootElement = document.getRootElement();// 5.获取根节点下的子节点的List集合List bookList = rootElement.getChildren();// 继续进行解析for (Element book : bookList) {Book bookEntity = new Book();System.out.println("======开始解析第" + (bookList.indexOf(book) + 1) + "书======");// 解析book的属性集合List attrList = book.getAttributes();// //知道节点下属性名称时,获取节点值// book.getAttributeValue("id");// 遍历attrList(针对不清楚book节点下属性的名字及数量)for (Attribute attr : attrList) {// 获取属性名String attrName = attr.getName();// 获取属性值String attrValue = attr.getValue();System.out.println("属性名:" + attrName + "----属性值:" + attrValue);if (attrName.equals("id")) {bookEntity.setId(attrValue);}}// 对book节点的子节点的节点名以及节点值的遍历List bookChilds = book.getChildren();for (Element child : bookChilds) {System.out.println("节点名:" + child.getName() + "----节点值:" + child.getValue());if (child.getName().equals("name")) {bookEntity.setName(child.getValue());} else if (child.getName().equals("author")) {bookEntity.setAuthor(child.getValue());} else if (child.getName().equals("year")) {bookEntity.setYear(child.getValue());} else if (child.getName().equals("price")) {bookEntity.setPrice(child.getValue());} else if (child.getName().equals("language")) {bookEntity.setLanguage(child.getValue());}}System.out.println("======结束解析第" + (bookList.indexOf(book) + 1) + "书======");booksList.add(bookEntity);bookEntity = null;System.out.println(booksList.size());System.out.println(booksList.get(0).getId());System.out.println(booksList.get(0).getName());}} catch (FileNotFoundException e) {e.printStackTrace();} catch (JDOMException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

3.4 DOM4J解析

    Dom4j 是一个简单的开源库,用于处理 XML、XPath 和 XSLT,它基于 Java 平台,使用 Java 的集合框架,全面集成了 DOM,SAX 和 JAXP;

    可以使用 DOM4J 进行 XML 文件的读、写操作;DOM4J 与 JDOM 一样都属于一个免费的 XML 开源组件,但是由于现在的开发框架中使用该技术较多,比如 Hibernate、Spring 等都使用 DOM4J 这个功能,所以作为介绍,大家可以对该组件有一个了解。并没有谁好谁坏,一般框架使用 DOM4J 较多,而我们平时如果要用则 JDOM 较常见。 可以发现 DOM4J 发挥了很多新特性,比如输出格式就可以很好解析。

    Dom4j是目前最流行、最好用的XML解析工具,解析XML的速度最快。

操作步骤:

    1、创建SAXReader:SAXReader reader = new SAXReader(); 

    2、创建文件对象:File in = new File("XXX.xml");

    3、通过reader读取xml文件到内存创建Document对象:Document dom = reader.read(in); 

    4、获取根节点:Element root = dom.getRootElement();

    5、获取子节点列表:List childNodes = root.elements();

    6、遍历子节点:Element node = childNodes.get(i);

    7、读取结点信息:
        1)结点属性值:node.attributeValue("属性名");
        2)结点名:node.getName();
        3)结点值:node.getValue();
        4)子结点文本值:node.elementText("子结点名")

import java.io.File;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import org.dom4j.Attribute;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;public class DOM4JTest {    private static ArrayList bookList = new ArrayList();    /**     * @param args     */    public static void main(String[] args) { // 解析books.xml文件 // 创建SAXReader的对象reader SAXReader reader = new SAXReader(); try {     // 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。     Document document = reader.read(new File("src/books.xml"));     // 通过document对象获取根节点bookstore     Element bookStore = document.getRootElement();     // 通过element对象的elementIterator方法获取迭代器     Iterator it = bookStore.elementIterator();   // 遍历迭代器,获取根节点中的信息(书籍)     while (it.hasNext()) {  System.out.println("=====开始遍历某一本书=====");  Element book = (Element) it.next();  // 获取book的属性名以及 属性值  List bookAttrs = book.attributes();  for (Attribute attr : bookAttrs) {      System.out.println("属性名:" + attr.getName() + "--属性值:"+ attr.getValue());  }  Iterator itt = book.elementIterator();  while (itt.hasNext()) {      Element bookChild = (Element) itt.next();      System.out.println("节点名:" + bookChild.getName() + "--节点值:" + bookChild.getStringValue());  }  System.out.println("=====结束遍历某一本书=====");     } } catch (DocumentException e) {     // TODO Auto-generated catch block     e.printStackTrace(); }    }}

四、DOM4J解析XML详解

    DOM4J是 dom4j.org 出品的一个开源 XML 解析包。DOM4J应用于 Java 平台,采用了 Java 集合框架并完全支持 DOM,SAX 和 JAXP。

    DOM4J 使用起来非常简单。只要你了解基本的 XML-DOM 模型,就能使用。

    Dom:把整个文档作为一个对象。

    DOM4J 最大的特色是使用大量的接口。它的主要接口都在org.dom4j里面定义:

接口名 作用
Attribute 定义了 XML 的属性。
Branch 指能够包含子节点的节点。如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为。
CDATA 定义了 XML CDATA 区域。
CharacterData 是一个标识接口,标识基于字符的节点。如CDATA,Comment, Text。
Comment 定义了 XML 注释的行为。
Document 定义了XML 文档。
DocumentType 定义 XML DOCTYPE 声明。
Element 定义XML 元素。
ElementHandler 定义了Element 对象的处理器。
ElementPath 被 ElementHandler 使用,用于取得当前正在处理的路径层次信息。
Entity 定义 XML entity。
Node 为dom4j中所有的XML节点定义了多态行为。
NodeFilter 定义了在dom4j 节点中产生的一个滤镜或谓词的行为(predicate)。
ProcessingInstruction 定义 XML 处理指令。
Text 定义 XML 文本节点。
Visitor 用于实现 Visitor模式。
XPath 在分析一个字符串后会提供一个 XPath 表达式。

接口之间的继承关系如下:

interface java.lang.Cloneable    interface org.dom4j.Node    interface org.dom4j.Attribute    interface org.dom4j.Branch    interface org.dom4j.Document    interface org.dom4j.Element    interface org.dom4j.CharacterData    interface org.dom4j.CDATA    interface org.dom4j.Comment    interface org.dom4j.Text    interface org.dom4j.DocumentType    interface org.dom4j.Entity    interface org.dom4j.ProcessingInstruction

4.1 XML文件内容

    1001小黑28北京市朝阳区1002小明22北京市昌平区1003王五21北京市房山区10006王伟伟16北京市昌平区

4.2 XML文档操作

4.2.1 读取XML文档

public static Document load(String filename) {      Document document = null;      try {   SAXReader saxReader = new SAXReader();  document = saxReader.read(new File(filename)); // 读取XML文件,获得document对象      } catch (Exception ex) {   ex.printStackTrace();      }      return document;  }  

4.2.2 获取根节点

    根节点是xml分析的开始,任何xml分析工作都需要从根开始。

//通过Document文档获取根节点Element root = dom.getRootElement(); 

4.2.3 新增一个节点以及其下的子节点与数据

//在根节点下面添加子节点menuElement menuElement = root.addElement("menu");   //在子节点menu下面添加子节点engNameElement engNameElement = menuElement.addElement("engName");  //给子节点engName设置文本内容engNameElement.setText(catNameEn);    Element chiNameElement = menuElement.addElement("chiName");    chiNameElement.setText(catName);  

4.2.4 写入XML文件

    注意文件操作的包装类是乱码的根源。

public static boolean doc2XmlFile(Document document, String filename) {      boolean flag = true;      try {   XMLWriter writer = new XMLWriter(new OutputStreamWriter(       new FileOutputStream(filename), "UTF-8"));   writer.write(document);   writer.close();      } catch (Exception ex) {   flag = false;   ex.printStackTrace();      }      System.out.println(flag);      return flag;  }  

    Dom4j通过XMLWriter将Document对象表示的XML树写入指定的文件,并使用OutputFormat格式对象指定写入的风格和编码方法。调用OutputFormat.createPrettyPrint()方法可以获得一个默认的pretty print风格的格式对象。对OutputFormat对象调用setEncoding()方法可以指定XML文件的编码方法。

public void writeTo(OutputStream out, String encoding)          throws UnsupportedEncodingException, IOException {      OutputFormat format = OutputFormat.createPrettyPrint();        format.setEncoding("utf-8");        XMLWriter writer = new XMLWriter(System.out, format);        writer.write(doc);        writer.flush();    }  

4.2.5 遍历xml节点

    对Document对象调用getRootElement()方法可以返回代表根节点的Element对象。拥有了一个Element对象后,可以对该对象调用elementIterator()方法获得它的子节点的Element对象们的一个迭代器。使用(Element)iterator.next()方法遍历一个iterator并把每个取出的元素转化为Element类型。

Element root = doc.getRootElement();  for (Iterator i = root.elementIterator(); i.hasNext();) {      Element el = (Element) i.next();      //...}

4.2.6 创建xml文件

Element booksElement = document.addElement("books");// 建立根节点  booksElement.addComment("This is a test for dom4j ");// 加入一行注释  Element bookElement = booksElement.addElement("book");// 添加一个book节点  bookElement.addAttribute("show", "yes");// 添加属性内容  Element titleElement = bookElement.addElement("title");// 添加文本节点  titleElement.setText("ajax in action");// 添加文本内容  try {      XMLWriter writer = new XMLWriter(new FileWriter(new File(fileName)));      writer.close();  } catch (Exception e) {      e.printStackTrace();  }

4.2.7 修改节点属性

public static void modifyXMLFile() {      String oldStr = "c:/text.xml";      String newStr = "c:/text1.xml";      Document document = null;      //修改节点的属性      try {   SAXReader saxReader = new SAXReader(); // 用来读取xml文档   document = saxReader.read(new File(oldStr)); // 读取xml文档   List list = document.selectNodes("/books/book/@show");// 用xpath查找节点book的属性   Iterator iter = list.iterator();   while (iter.hasNext()) {       Attribute attribute = (Attribute) iter.next();       if (attribute.getValue().equals("yes"))     attribute.setValue("no");   }      } catch (Exception e) {   e.printStackTrace();      }      //修改节点的内容      try {   SAXReader saxReader = new SAXReader(); // 用来读取xml文档   document = saxReader.read(new File(oldStr)); // 读取xml文档   List list = document.selectNodes("/books/book/title");// 用xpath查找节点book的内容   Iterator iter = list.iterator();   while (iter.hasNext()) {       Element element = (Element) iter.next();       element.setText("xxx");// 设置相应的内容   }      } catch (Exception e) {   e.printStackTrace();      }      try {   XMLWriter writer = new XMLWriter(new FileWriter(new File(newStr)));   writer.write(document);   writer.close();      } catch (Exception ex) {   ex.printStackTrace();      }  }  

4.2.8 删除节点

public static void removeNode() {      String oldStr = "c:/text.xml";      String newStr = "c:/text1.xml";      Document document = null;      try {   SAXReader saxReader = new SAXReader();// 用来读取xml文档   document = saxReader.read(new File(oldStr));// 读取xml文档   List list = document.selectNodes("/books/book");// 用xpath查找对象   Iterator iter = list.iterator();   while (iter.hasNext()) {       Element bookElement = (Element) iter.next();       // 创建迭代器,用来查找要删除的节点,迭代器相当于指针,指向book下所有的title节点       Iterator iterator = bookElement.elementIterator("title");       while (iterator.hasNext()) {    Element titleElement = (Element) iterator.next();    if (titleElement.getText().equals("ajax in action")) {        bookElement.remove(titleElement);    }       }   }      } catch (Exception e) {   e.printStackTrace();      }      try {   XMLWriter writer = new XMLWriter(new FileWriter(new File(newStr)));   writer.write(document);   writer.close();      } catch (Exception ex) {   ex.printStackTrace();      }  }  

4.3 DOM4J读取XML方式一

import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.File;import java.util.List;public class Test_Reader01 {    public static void main(String[] args) { //1.用SAXReader类,创建一个解析器 SAXReader sr = new SAXReader(); //2.获取需要被解析的xml文档 File file = new File("Students.xml"); try {     //3.用解析器解析文档,生成dom4j文档对象     Document doc = sr.read(file);     //4.用文档对象获取根元素对象 获取根节点root     Element rootEle = doc.getRootElement();//Students对象     //5.使用根节点获取所有的子节点     List stuList = rootEle.elements();//所有的Student     //6.遍历输出     for (Element e : stuList) {  List messList = e.elements();//获取Student的元素  for (Element ee : messList) {      System.out.println(ee.getName() + "\t" + ee.getText());  }  System.out.println("*************************");     } } catch (DocumentException e) {     // TODO Auto-generated catch block     e.printStackTrace(); }    }}

4.4 DOM4J读取XML方式二

import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.File;import java.util.List;public class Test_Reader02 {    public static void main(String[] args) { //1、用SAXReader类,创建一个解析器 SAXReader sr = new SAXReader(); //2、获取需要被解析的xml文档 File file = new File("Students.xml"); try {     //3、用解析器解析文档,生成dom4j文档对象     Document doc = sr.read(file);     //4、直接获取到Student学生节点      List stuList = doc.selectNodes("/Students/Student");//Student     System.out.println("所有学生的信息:");     for (Element e : stuList) {  //直接拿Student下来的值     //去学生下面指定元素的文本值  参数放的是标签名(元素名)  System.out.println(e.elementText("sid") + "\t" +   e.elementText("sname") + "\t" + e.elementText("sage"));     } } catch (DocumentException e) {     // TODO Auto-generated catch block     e.printStackTrace(); }    }}

4.5 DOM4J添加XML数据

import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.SAXReader;import org.dom4j.io.XMLWriter;import java.io.File;import java.io.FileWriter;public class Test_Add {    public static void main(String[] args) throws Exception { // TODO Auto-generated method stub //1、用SAXReader类,创建一个解析器 SAXReader sr = new SAXReader(); //2、获取需要被解析的xml文档 File file = new File("Students.xml"); //3、用解析器解析文档,生成dom4j文档对象 Document doc = sr.read(file); Element rootEle = doc.getRootElement(); //4、添加节点、添加子节点及文本 Element stuEle = rootEle.addElement("Student"); stuEle.addElement("sid").addText("10011"); stuEle.addElement("sname").addText("孙小名"); stuEle.addElement("sage").addText("18"); stuEle.addElement("saddress").addText("北京市昌平区"); //5、输出格式化 OutputFormat of = OutputFormat.createPrettyPrint(); of.setEncoding("utf-8"); //6、定义XML的输出流 XMLWriter xw = new XMLWriter(new FileWriter(file), of); xw.write(doc); xw.close();    }}

4.6 DOM4J删除XML数据

import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.SAXReader;import org.dom4j.io.XMLWriter;import java.io.File;import java.io.FileWriter;import java.util.List;import java.util.Scanner;public class Test_Delete {    public static void main(String[] args) throws Exception { // TODO Auto-generated method stub Scanner sc = new Scanner(System.in); System.out.println("请输入要删除学生的学号:"); String sid = sc.next(); File file = new File("Students.xml"); SAXReader sr = new SAXReader(); Document doc = sr.read(file); //直接选取学生元素  Node的子类是Element List stuList = doc.selectNodes("/Students/Student"); for (Element e : stuList) {     //元素里面的值和输入进来的值     //elementText(String s)  获取指定元素的文本值     if (e.elementText("sid").equals(sid)) {  e.getParent().remove(e);  System.out.println("删除成功");     } } //输出格式化 OutputFormat of = OutputFormat.createPrettyPrint(); of.setEncoding("utf-8");  //声明一个输出流 XMLWriter xw = new XMLWriter(new FileWriter(file), of); xw.write(doc); xw.close();    }}

4.7 DOM4J修改XML数据

import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.util.List;import java.util.Scanner;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.SAXReader;import org.dom4j.io.XMLWriter;public class Test_Update {    public static void main(String[] args)throws Exception { // TODO Auto-generated method stub Scanner sc=new Scanner(System.in); System.out.println("请输入要修改学生的学号:"); String sid=sc.next(); File file=new File("Students.xml"); SAXReader sr=new SAXReader(); Document doc = sr.read(file); //直接选取学生元素  Node的子类是Element List stuList=doc.selectNodes("/Students/Student"); for(Element e:stuList){     //元素里面的值和输入进来的值     //elementText(String s)  获取指定元素的文本值     if(e.elementText("sid").equals(sid)){  //如果学号判断成功,则可以对这个学生的信息进行修改  //element(String s) 获取指定元素  e.element("sname").setText("小明");  e.element("sage").setText("22");  System.out.println("修改成功!");     } } //保存操作 OutputFormat of=OutputFormat.createPrettyPrint(); of.setEncoding("utf-8");  //声明一个输出流 XMLWriter xw=new XMLWriter(new FileWriter(file),of); xw.write(doc); xw.close();    }}

五、总结

    DOM4J性能最好,连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J。
    
    JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM。虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。

    SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。