Java基础系列17:使用DOM、SAX、JDOM、DOM4J解析XML文件详解

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介:

一 简介

在Java中,可以使用多种方式来解析XML文件,其中最常见的可能就是DOM、SAX、JDOM、DOM4J这四种方式了。其中,DOM和SAX这两种解析XML文件的方式有jdk自带的API,因此不需要额外引入第三方的jar包。与之相反的是,JDOM和DOM4J这两种解析方式都是第三方开源项目,因此在使用这两种方式解析XML文件时需要额外引入相关jar包

(1)DOM

DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合,这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作

因此在使用DOM这种方式来解析XML文件时,解析器需要将整个XML文件读到内存中,形成一个树形结构方便后面的操作

  • 优点: 整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种操作

  • 缺点: 将整个文档调入内存(包括无用的节点),浪费时间和内存,如果XML过大容易出现内存溢出问题

(2)SAX

由于DOM解析XML文件时需要一次性读入整个文件,当文件过大时有诸多不足之处,因此为了解决这个问题,出现了SAX这种基于事件驱动的解析方式

SAX解析XML文件通过从上往下依次不断载入内容到内存中,当解析器发现元素的开始标志、结束标志、文本、文档的开始标志、文档的结束标志等相关标志时,将会触发一些对应的事件,我们需要做的就是在这些事件的方法中编写自定义代码,用于保存获取到的数据

  • 优点:不用事先载入整个文档,占用资源(内存)少;使用SAX方式解析编写的代码要比使用DOM解析编写的代码少

  • 缺点:不是持久的;事件过后,若没保存数据,那么数据就丢了;无状态性;从事件中只能得到文本,但不知该文本属于哪个元素

(3)JDOM

使用JDOM来解析XML文件跟使用DOM来解析从代码上来说解析思路是差不多的。JDOM与DOM主要有两方面不同:首先,JDOM仅使用具体类而不使用接口,这在某些方面简化了API,但是也限制了灵活性。其次是JDOM的API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用

  • 优点:开源项目;比DOM容易理解

  • 缺点:JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档

(4)DOM4J

DOM4J 是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的 Java 软件都在使用 DOM4J 来读写 XML

由于DOM4J无论在性能方面还是代码编写方面都是很强大的,特别是当XML文件很大时使用DOM4J来解析也会有较高的效率。因此,建议平时需要解析XML文件可以考虑尽可能使用DOM4J来解析。当然如果文件非常小的话使用DOM来解析也是可以的

优点:

  • 开源项目

  • DOM4J是JDOM的一种智能分支,它合并了需要超出基本XML文档的功能

  • 具有性能优异、灵活性好、简单易用等特点

二 DOM解析XML文件

(1)在进行代码编写测试之前,需要准备一个XML文件,我这里准备的文件是:demo1.xml

demo1.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<? xml  version = "1.0"  encoding = "UTF-8"  ?>
< employees >
     < user  id = "1" >
         < name >zifangsky</ name >
         < age >10</ age >
         < sex >male</ sex >
         < contact >https://www.zifangsky.cn</ contact >
     </ user >
     < user  id = "2" >
         < name >admin</ name >
         < age >20</ age >
         < sex >male</ sex >
         < contact >https://www.tar.pub</ contact >
     </ user >
</ employees >

(2)代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package  cn.zifangsky.xml;
 
import  javax.xml.parsers.DocumentBuilder;
import  javax.xml.parsers.DocumentBuilderFactory;
 
import  org.w3c.dom.Document;
import  org.w3c.dom.Element;
import  org.w3c.dom.NamedNodeMap;
import  org.w3c.dom.Node;
import  org.w3c.dom.NodeList;
 
public  class  DomParseTest {
 
     public  static  void  main(String[] args) {
         DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
         try  {
             DocumentBuilder dBuilder = dFactory.newDocumentBuilder();
             // 加载一个xml文件
             Document document = dBuilder
                     .parse( "src/cn/zifangsky/xml/demo1.xml" );
             // 获取user节点集合
             NodeList userList = document.getElementsByTagName( "user" );
             int  userListLength = userList.getLength();
             System.out.println( "此xml文件一共有"  + userListLength +  "个'user'节点\n" );
 
             // 遍历
             for  ( int  i =  0 ; i < userListLength; i++) {
                 // 通过item方法获取指定的节点
                 Node userNode = userList.item(i);
 
                 // *********************解析属性***********************
 
                 // 获取该节点的所有属性值,如:id="1"
                 NamedNodeMap userAttributes = userNode.getAttributes();
                 System.out.println( "'user'节点"  + i +  "有"
                         + userAttributes.getLength() +  "个属性:" );
                 /**
                  * 1 在不清楚有哪些属性的情况下可以遍历所有属性,
                  * 并获取每个属性对应的属性名和属性值
                  * */
                 for  ( int  j =  0 ; j < userAttributes.getLength(); j++) {
                     // 'user'节点的每个属性组成的节点
                     Node attrnNode = userAttributes.item(j);
                     System.out.println( "属性"  + j +  ": 属性名: "
                             + attrnNode.getNodeName() +  " ,属性值: "
                             + attrnNode.getNodeValue());
                 }
                 /**
                  * 2 在知道有哪些属性值的情况下,可以获取指定属性名的属性值
                  * */
                 Element userElement = (Element) userList.item(i);
                 System.out.println( "属性为'id'的对应值是: "
                         + userElement.getAttribute( "id" ));
 
                 // *********************解析子节点************************
                 NodeList childNodes = userNode.getChildNodes();
                 System.out.println( "\n该节点一共有"  + childNodes.getLength()
                         "个子节点,分别是:" );
 
                 // 遍历子节点
                 for  ( int  k =  0 ; k < childNodes.getLength(); k++) {
                     Node childNode = childNodes.item(k);
                     // 从输出结果可以看出,每行后面的换行符也被当做了一个节点,因此是:4+5=9个子节点
                     // System.out.println("节点名: " + childNode.getNodeName() +
                     // ",节点值: " + childNode.getTextContent());
                     // 仅取出子节点中的'ELEMENT_NODE',换行符组成的Node是'TEXT_NODE'
                     if  (childNode.getNodeType() == Node.ELEMENT_NODE) {
                         // System.out.println("节点名: " + childNode.getNodeName()
                         // + ",节点值: " + childNode.getTextContent());
                         // 最低一层是文本节点,节点名是'#text'
                         System.out.println( "节点名: "  + childNode.getNodeName()
                                 ",节点值: "
                                 + childNode.getFirstChild().getNodeValue());
                     }
                 }
 
                 System.out.println( "***************************" );
             }
 
         catch  (Exception e) {
             e.printStackTrace();
         }
 
     }
 
}

从上面的代码可以看出,在使用DOM来解析XML文件时一般需要做以下几步操作:

  1. 创建一个文档构建器工厂(DocumentBuilderFactory)实例

  2. 通过上面的DocumentBuilderFactory生成一个新的文档构建器(DocumentBuilder)

  3. 使用上面的DocumentBuilder解析(parse)一个XML文件,生成文档树(Document)

  4. 通过Document获取指定id的节点或根据节点名获取所有符合条件的节点集合

  5. 遍历每个节点,可以获取该节点的属性、属性值等相关参数

  6. 如果该节点还存在子节点,可以根据上面的方式继续遍历它的所有子节点

(3)上面的代码输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
此xml文件一共有2个'user'节点
 
'user'节点0有1个属性:
属性0: 属性名: id ,属性值: 1
属性为'id'的对应值是: 1
 
该节点一共有9个子节点,分别是:
节点名: name,节点值: zifangsky
节点名: age,节点值: 10
节点名: sex,节点值: male
节点名: contact,节点值: https://www.zifangsky.cn
***************************
'user'节点1有1个属性:
属性0: 属性名: id ,属性值: 2
属性为'id'的对应值是: 2
 
该节点一共有9个子节点,分别是:
节点名: name,节点值: admin
节点名: age,节点值: 20
节点名: sex,节点值: male
节点名: contact,节点值: https://www.tar.pub
***************************

三 SAX解析XML文件

在进行本次测试时,并不引入其他XML文件,仍然使用上面的demo1.xml文件

由于SAX解析XML文件跟DOM不同,它并不是将整个文档都载入到内存中。解析器在解析XML文件时,通过逐步载入文档,从上往下一行行的解析XML文件,在碰到文档开始标志、节点开始标志、文本文档、节点结束标志、文档结束标志时进行对应的事件处理。因此,我们首先需要构造一个这样的解析处理器来申明:当解析到这些标志时,我们需要进行怎样的自定义处理

(1)解析处理器SAXParseHandler.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package  cn.zifangsky.xml;
 
import  org.xml.sax.Attributes;
import  org.xml.sax.SAXException;
import  org.xml.sax.helpers.DefaultHandler;
 
public  class  SAXParseHandler  extends  DefaultHandler {
 
     /**
      * 用来遍历XML文件的开始标签
      * */
     @Override
     public  void  startElement(String uri, String localName, String qName,
             Attributes attributes)  throws  SAXException {
         super .startElement(uri, localName, qName, attributes);
         
         //解析'user'元素的属性值
//      if(qName.equals("user"))
//          System.out.println("'user'元素的id属性值是:" + attributes.getValue("id"));
         
         //遍历并打印元素的属性
         int  length = attributes.getLength();
         if (length >  0 ){
             System.out.println( "元素'"  + qName +  "'的属性是:" );
             
             for ( int  i= 0 ;i<length;i++){
                 System.out.println( "    属性名:"  + attributes.getQName(i) +  ",属性值: "  + attributes.getValue(i));
             }
             System.out.println();
         }
         
         System.out.print( "<"  + qName +  ">" );
 
     }
 
     /**
      * 用来遍历XML文件的结束标签
      * */
     @Override
     public  void  endElement(String uri, String localName, String qName)
             throws  SAXException {
         super .endElement(uri, localName, qName);
         
         System.out.println( "<"  + qName +  "/>" );
     }
     
     /**
      * 文本内容
      * */
     public  void  characters( char [] ch,  int  start,  int  length)
             throws  SAXException {
         super .characters(ch, start, length);
         String value =  new  String(ch, start, length).trim();
         if (!value.equals( "" ))
             System.out.print(value);
     }
 
     /**
      * 用来标识解析开始
      * */
     @Override
     public  void  startDocument()  throws  SAXException {
         System.out.println( "SAX解析开始" );
         super .startDocument();
     }
     
     /**
      * 用来标识解析结束
      * */
     @Override
     public  void  endDocument()  throws  SAXException {
         System.out.println( "SAX解析结束" );
         super .endDocument();
     }
 
}

关于上面代码的一些含义我这里就不再做解释了,可以自行参考注释内容

(2)测试:

SAXParseTest.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  cn.zifangsky.xml;
 
import  javax.xml.parsers.SAXParser;
import  javax.xml.parsers.SAXParserFactory;
 
public  class  SAXParseTest {
 
     public  static  void  main(String[] args) {
         SAXParserFactory sFactory = SAXParserFactory.newInstance();
         try  {
             SAXParser saxParser = sFactory.newSAXParser();
             //创建自定义的SAXParseHandler解析类
             SAXParseHandler saxParseHandler =  new  SAXParseHandler();
             saxParser.parse( "src/cn/zifangsky/xml/demo1.xml" , saxParseHandler);
 
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
 
}

从上面的代码可以看出,使用SAX解析XML文件时,一共传递进去了两个参数,分别是:XML文件路径和前面定义的解析处理器。有了具体的XML文件以及对应的处理器来处理对应的标志事情,因此SAX这种解析方式就可以顺利地进行解析工作了

(3)上面测试的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SAX解析开始
<employees>元素'user'的属性是:
     属性名:id,属性值: 1
 
<user><name>zifangsky<name/>
<age>10<age/>
<sex>male<sex/>
<contact>https://www.zifangsky.cn<contact/>
<user/>
元素'user'的属性是:
     属性名:id,属性值: 2
 
<user><name>admin<name/>
<age>20<age/>
<sex>male<sex/>
<contact>https://www.tar.pub<contact/>
<user/>
<employees/>
SAX解析结束

四 JDOM解析XML文件

跟前面两种解析方式不同的是,使用JDOM来解析XML文件需要下载额外的jar包

(1)下载jar包并导入到项目中:

下载地址:http://www.jdom.org/downloads/index.html

目前最新版本是:JDOM 2.0.6

然后将下载得到的“jdom-2.0.6.jar”文件导入到测试项目中

注:关于如何在一个Java项目中导入额外的jar,这里将不多做解释,不太会的童鞋可以自行百度

(2)测试代码:

JDOMTest.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package  cn.zifangsky.xml;
 
import  java.util.List;
 
import  org.jdom2.Document;
import  org.jdom2.Element;
import  org.jdom2.input.SAXBuilder;
 
public  class  JDOMTest {
 
     /**
      * @param args
      */
     public  static  void  main(String[] args) {
         SAXBuilder saxBuilder =  new  SAXBuilder();
         try  {
             Document document = saxBuilder.build( "src/cn/zifangsky/xml/demo1.xml" );
             
             //获取XML文件的根节点
             Element rootElement = document.getRootElement();
//          System.out.println(rootElement.getName());
             List<Element> usersList = rootElement.getChildren();   //获取子节点
             for (Element u : usersList){
//              List<Attribute> attributes = u.getAttributes();
//              for(Attribute attribute : attributes){
//                  System.out.println("属性名:" + attribute.getName() + ",属性值:" + attribute.getValue());
//              }
                 System.out.println( "'id'的值是: "  + u.getAttributeValue( "id" ));
             }
             
             
         } catch  (Exception e) {
             e.printStackTrace();
         }
 
     }
 
}

从上面的代码可以看出,使用JDOM来解析XML文件,主要需要做以下几个步骤:

  1. 新建一个SAXBuilder

  2. 通过SAXBuilder的build方法传入一个XML文件的路径得到Document

  3. 通过Document的getRootElement方法获取根节点

  4. 通过getChildren方法获取根节点的所有子节点

  5. 然后是遍历每个子节点,获取属性、属性值、节点名、节点值等内容

  6. 如果该节点也有子节点,然后同样可以通过getChildren方法获取该节点的子节点

  7. 后面的步骤跟上面一样,不断递归到文本节点截止

(3)上面测试的输出如下:

1
2
'id'的值是: 1
'id'的值是: 2

五 DOM4J解析XML文件

jar包下载地址:https://sourceforge.net/projects/dom4j/files/

同样,在使用DOM4J解析XML文件时需要往项目中引入“dom4j-1.6.1.jar”文件

(1)一个简单实例:

i)DOM4JTest.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package  cn.zifangsky.xml;
 
import  java.io.File;
import  java.util.Iterator;
import  java.util.List;
 
import  org.dom4j.Attribute;
import  org.dom4j.Document;
import  org.dom4j.Element;
import  org.dom4j.io.SAXReader;
 
public  class  DOM4JTest {
 
     public  static  void  main(String[] args) {
         SAXReader reader =  new  SAXReader();
         try  {
             Document document = reader.read( new  File( "src/cn/zifangsky/xml/demo1.xml" ));
             //获取XML文件的根节点
             Element rootElement = document.getRootElement();
             System.out.println(rootElement.getName());
             
             //通过elementIterator方法获取迭代器
             Iterator<Element> iterator = rootElement.elementIterator();
             //遍历
             while (iterator.hasNext()){
                 Element user = iterator.next();
                 //获取属性并遍历
                 List<Attribute> aList = user.attributes();
             
                 for (Attribute attribute : aList){
                     System.out.println( "属性名:"  + attribute.getName() +  ",属性值:"  + attribute.getValue());
                 }
                 
                 //子节点
                 Iterator<Element> childList = user.elementIterator();
                 while (childList.hasNext()){
                     Element child = childList.next();
//                  System.out.println(child.getName() + " : " + child.getTextTrim());
                     System.out.println(child.getName() +  " : "  + child.getStringValue());
                 }
             }
         
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
 
}

从上面的代码可以看出,跟前面的JDOM解析方式流程是差不多的,并且关键地方也有注释,因此这里就不多做解释了

ii)上面的代码输出如下:

1
2
3
4
5
6
7
8
9
10
11
employees
属性名:id,属性值:1
name : zifangsky
age : 10
sex : male
contact : https://www.zifangsky.cn
属性名:id,属性值:2
name : admin
age : 20
sex : male
contact : https://www.tar.pub

(2)将XML文件解析成Java对象:

i)为了方便测试,这里准备一个新的XML文件:

demo2.xml:

1
2
3
4
5
6
7
8
9
<? xml  version = "1.0"  encoding = "UTF-8"  ?>
< user  id = "2" >
     < name >zifangsky</ name >
     < age >100</ age >
     < sex >男</ sex >
     < contact >https://www.zifangsky.cn</ contact >
     < ownPet  id = "1" >旺财</ ownPet >
     < ownPet  id = "2" >九头猫妖</ ownPet >
</ user >

ii)同时准备一个Java实体类,恰好跟上面的XML文件中的属性相对应:

User.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package  cn.zifangsky.xml;
 
import  java.util.List;
 
public  class  User {
     private  String name;
     private  String sex;
     private  int  age;
     private  String contact;
     private  List<String> ownPet;
 
     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;
     }
 
     public  String getContact() {
         return  contact;
     }
 
     public  void  setContact(String contact) {
         this .contact = contact;
     }
 
     protected  List<String> getOwnPet() {
         return  ownPet;
     }
 
     protected  void  setOwnPet(List<String> ownPet) {
         this .ownPet = ownPet;
     }
 
     @Override
     public  String toString() {
         return  "User [name="  + name +  ", sex="  + sex +  ", age="  + age
                 ", contact="  + contact +  ", ownPet="  + ownPet +  "]" ;
     }
}

iii)测试代码:

XMLtoJava.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package  cn.zifangsky.xml;
 
import  java.io.File;
import  java.util.ArrayList;
import  java.util.List;
 
import  org.dom4j.Document;
import  org.dom4j.Element;
import  org.dom4j.io.SAXReader;
 
public  class  XMLtoJava {
 
     public  User parseXMLtoJava(String xmlPath){
         User user =  new  User();
         List<String> ownPet =  new  ArrayList<String>();
         
         SAXReader saxReader =  new  SAXReader();
         try  {
             Document document = saxReader.read( new  File(xmlPath));
             Element rootElement = document.getRootElement();   //获取根节点
             
             List<Element> children = rootElement.elements();   //获取根节点的子节点
             //遍历
             for (Element child : children){
                 String elementName = child.getName();   //节点名
                 String elementValue = child.getStringValue();   //节点值
                 switch  (elementName) {
                 case  "name" :
                     user.setName(elementValue);
                     break ;
                 case  "sex" :
                     user.setSex(elementValue);
                     break
                 case  "age" :
                     user.setAge(Integer.valueOf(elementValue));
                     break ;
                 case  "contact" :
                     user.setContact(elementValue);
                     break
                 case  "ownPet" :
                     ownPet.add(elementValue);
                     break
                 default :
                     break ;
                 }
     
             }
             user.setOwnPet(ownPet);
 
         catch  (Exception e) {
             e.printStackTrace();
         }
         return  user;
     }
     
     public  static  void  main(String[] args) {
         XMLtoJava demo =  new  XMLtoJava();
         User user = demo.parseXMLtoJava( "src/cn/zifangsky/xml/demo2.xml" );
         System.out.println(user);
     }
 
}

经过前面的分析之后,上面这个代码也是很容易理解的:通过遍历节点,如果节点名跟Java类中的某个属性名相对应,那么就将节点值赋值给该属性

iv)上面的代码输出如下:

1
User [name=zifangsky, sex=男, age=100, contact=https://www.zifangsky.cn, ownPet=[旺财, 九头猫妖]]

(3)解析一个XML文件并尽可能原样输出:

DOM4JTest2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package  cn.zifangsky.xml;
 
import  java.io.File;
import  java.util.List;
 
import  org.dom4j.Attribute;
import  org.dom4j.Document;
import  org.dom4j.Element;
import  org.dom4j.io.SAXReader;
 
public  class  DOM4JTest2 {
 
     /**
      * 解析XML文件并尽可能原样输出
     
      * @param xmlPath
      *            待解析的XML文件路径
      * @return null
      * */
     public  void  parse(String xmlPath) {
         SAXReader saxReader =  new  SAXReader();
         try  {
             Document document = saxReader.read( new  File(xmlPath));
             Element rootElement = document.getRootElement();
 
             print(rootElement,  0 );
 
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
 
     /**
      * 打印一个XML节点的详情
     
      * @param element
      *            一个XML节点
      * @param level
      *            用于判断xml节点前缩进多少的标识,每深入一层则多输出4个空格
      * @return null
      * */
     public  void  print(Element element,  int  level) {
         List<Element> elementList = element.elements();  // 当前节点的子节点List
 
         // 空格
         StringBuffer spacebBuffer =  new  StringBuffer( "" );
         for  ( int  i =  0 ; i < level; i++)
             spacebBuffer.append( "    " );
         String space = spacebBuffer.toString();
 
         // 输出开始节点及其属性值
         System.out.print(space +  "<"  + element.getName());
         List<Attribute> attributes = element.attributes();
         for  (Attribute attribute : attributes)
             System.out.print( " "  + attribute.getName() +  "=\""
                     + attribute.getText() +  "\"" );
 
         // 有子节点
         if  (elementList.size() >  0 ) {
             System.out.println( ">" );
             // 遍历并递归
             for  (Element child : elementList) {
                 print(child, level +  1 );
             }
             // 输出结束节点
             System.out.println(space +  "</"  + element.getName() +  ">" );
 
         else  {
             // 如果节点没有文本则简化输出
             if  (element.getStringValue().trim().equals( "" ))
                 System.out.println( " />" );
             else
                 System.out.println( ">"  + element.getStringValue() +  "</"
                         + element.getName() +  ">" );
         }
 
     }
 
     public  static  void  main(String[] args) {
         DOM4JTest2 test2 =  new  DOM4JTest2();
         test2.parse( "src/cn/zifangsky/xml/demo3.xml" );
 
     }
 
}

这段代码同样没有什么新的东西,原理就是利用递归来不断进行解析输出,注意一下不同层次的节点的缩进即可。刚开始测试时建议用一些结构比较简单的代码,如上面的demo1.xml和demo2.xml文件。在测试没问题时可以选择一些复杂的XML文件来测试是否能够正常输出,比如:

demo3.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<? xml  version = "1.0"  encoding = "UTF-8" ?>
< application  xmlns = "http://wadl.dev.java.net/2009/02"
     xmlns:xs = "http://www.w3.org/2001/XMLSchema" >
     < grammars  />
     < resources  base = "http://localhost:9080/Demo/services/json/checkCode" >
         < resource  path = "/" >
             < resource  path = "addCheckCode" >
                 < method  name = "POST" >
                     < request >
                         < representation  mediaType = "application/octet-stream"  />
                     </ request >
                     < response >
                         < representation  mediaType = "application/xml" >
                             < param  name = "result"  style = "plain"  type = "xs:int"  />
                         </ representation >
                         < representation  mediaType = "application/json" >
                             < param  name = "result"  style = "plain"  type = "xs:int"  />
                         </ representation >
                     </ response >
                 </ method >
             </ resource >
             < resource  path = "findCheckCodeByProfileId" >
                 < method  name = "POST" >
                     < request >
                         < representation  mediaType = "application/octet-stream" >
                             < param  name = "request"  style = "plain"  type = "xs:long"  />
                         </ representation >
                     </ request >
                     < response >
                         < representation  mediaType = "application/xml"  />
                         < representation  mediaType = "application/json"  />
                     </ response >
                 </ method >
             </ resource >
         </ resource >
     </ resources >
</ application >

为什么我在标题上说的是尽可能原样输出,其原因就是上面那段解析代码在碰到下面这种XML节点时,输出就不一样了:

1
2
3
4
<dc:creator><![CDATA[admin]]></dc:creator>
<category><![CDATA[运维]]></category>
<category><![CDATA[zabbix]]></category>
<category><![CDATA[端口]]></category>

这段XML文档节点最后输出如下:

1
2
3
4
<creator>admin</creator>
<category>运维</category>
<category>zabbix</category>
<category>端口</category>



本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1861357,如需转载请自行联系原作者
相关文章
|
1月前
|
JavaScript 前端开发 Go
CSS 与 JS 对 DOM 解析和渲染的影响
【10月更文挑战第16天】CSS 和 JS 会在一定程度上影响 DOM 解析和渲染,了解它们之间的相互作用以及采取适当的优化措施是非常重要的。通过合理的布局和加载策略,可以提高网页的性能和用户体验,确保页面能够快速、流畅地呈现给用户。在实际开发中,要根据具体情况进行权衡和调整,以达到最佳的效果。
|
14天前
|
Java Maven
maven项目的pom.xml文件常用标签使用介绍
第四届人文,智慧教育与服务管理国际学术会议(HWESM 2025) 2025 4th International Conference on Humanities, Wisdom Education and Service Management
69 8
|
1月前
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
1月前
|
JavaScript 前端开发 算法
React 虚拟 DOM 深度解析
【10月更文挑战第5天】本文深入解析了 React 虚拟 DOM 的工作原理,包括其基础概念、优点与缺点,以及 Diff 算法的关键点。同时,分享了常见问题及解决方法,并介绍了作者在代码/项目上的成就和经验,如大型电商平台的前端重构和开源贡献。
58 3
|
1月前
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
1月前
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
9天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
34 2
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
70 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
1月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
62 0
下一篇
无影云桌面