XML
本章目标
- 理解XML概念及优势
- 会编写格式良好的XML文档
- 了解XML中特殊字符的处理方式
- 了解解析器概念
- 了解DOM树节点构造
- 会使用DOM操作XML数据(添加/保存)
1. 使用XML存储数据
关键步骤如下:
- 了解XML文档结构
- 编写格式良好的XML文档
- 编写保存图书信息的XML文档
1.1 认识XML
XML是Extensible Markup Language即可扩展标记语言的缩写,是一种简单的数据存储语言,使用一系列简单的标记来描述数据。XML技术应用广泛,最基本的如网站、应用程序的配置信息一般都采用XML文件描述。
XML特点如下:
- XML与操作系统、编程语言的开发平台无关
- 规范统一
作用如下:
- 数据交互
- 配置应用程序和网站
- Ajax基石
1.2 XML 文档结构
先来了解XML文档结构,如下所示是描述图书信息的XML代码:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<!--图书信息 -->
<book id="bk101">
<author>王珊</author>
<title>.NET高级编程</title>
<description>包含C#框架和网络编程等</description>
</book>
<book id="bk102">
<author>李明明</author>
<title>XML基础编程</title>
<description>包含XML基础概念和基本作用</description>
</book>
</books>
-
XML声明
<?xml version="1.0" encoding="UTF-8"?>
表示XML声明,用以标明该文件是一个XML文档。XML文档总是以XML声明开始,它定义了XML的版本和使用的编码格式等信息。XML声明由以下几个部分组成:
- version:文档符合XML 1.0规范
- encoding:文档字符编码,默认为“UTF-8”。
对于任何一个XML文档,其声明部分都是固定的格式。
-
标签
在XML中,用尖括号<>
括起来的各种标签(Tag)来标记数据,标签需成对使用来界定字符数据,例如<author>王珊</author>
这一对标签中,<author>
是开始标签,</author>
结束标签,"王珊"
是标签描述的内容,表示作者信息。XML文件可以包含任意数据。 -
根元素
每个XML文档必须有且仅有一个根元素,如<books></books>
根元素的特点如下:
- 根元素是一个完全包括文档中其他所有元素的元素。
- 根元素的起始标签要放在所有其他元素的起始标签之前。
- 根元素的结束标签要放在所有其他元素的结束标签之后。
-
元素
XML文档的主要部分是元素。元素由开始标签、元素内容和结束标签组成。元素内容可以包含子元素。字符数据等。如<author>王珊</author>
就是一个元素。元素的命名规则如下:
- 名称中可以包含字母、数字或者其他字符
- 名称不能以数字或者标点符号开始
- 名称不能以字符xml(或者XML,Xml)开始
- 名称中不能包含空格
-
属性
在描述图书信息的XML文档中,<book id="bk101">
标签使用id属性描述图书的编号信息。属性定义语法格式如下。<元素名 属性名="属性值">
属性值用一对双引号包含起来。
注意:
① 一个元素可以有多个属性,它的基本格式为<元素名 属性名="属性值" 属性名="属性值">
,多个属性之间用空格隔开② 属性值中不能直接包含
<
、"
、&
等字符③ 属性可以加在任何一个元素的起始标签上,但不能加在结束标签上。
-
XML中的特殊字符的处理
(1) 特殊字符需要进行转义,也就是使用XML中的预定义实体代替这些字符,对应关系如图所示:实体名称 字符 <
<
>
>
&
&
"
"
'
'
(2) 如果在元素的文本中有大量的特殊字符,可以使用
CDATA
节处理。CDATA
节中的所有字符都会被当作元素字符数据的常量部分,而不是XML标签。定义CDATA
节的语法格式如下:<![CDATA[要显示的字符]]>
-
XML中注释
注释的语法格式如下:<!-- 注释内容 -->
-
格式良好的XML文档,需要遵循如下规则
- 必须有XML声明语句
- 必须有且仅有一个根元素
- 标签大小写敏感
- 属性值用双引号包含起来
- 标签成对出现
- 元素正确嵌套
1.3 XML优势
XML的优势主要体现在以下几点:
- 数据存储
- 数据交换
- 数据配置
1.4 XML中的命名空间
命名空间在XML文档中可以用作元素或属性名称的名称集合,它们用来标识来自特定域(标准组织、公司、行业)的名称。
-
命名空间的必要性。XML解析器在解析XML文档时,对于重名的元素,可能出现解析冲突。命名空间有助于标准化元素和属性,并为它们加上唯一的标识
-
声明命名空间。语法格式如下
xmlns:[prefix]="[命名空间的URI]"
- prefix 是前缀名称,它用作命名空间的别名
- xmlns 是保留属性
-
属性和命名空间。除非带有前缀,否则属性属于它们的元素所在的命名空间
-
命名空间的应用。示例如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"> <context:component-scan base-package="cn.smbms.controller"/> <aop:config></aop:config> </beans>
在示例中声明了两个命名空间,别名分别是
"context"
和"aop"
。对应URI分别是http://www.springframework.org/schema/context
,http://www.springframework.org/schema/aop
,第一个component-scan
加上了前缀context
,代码它属于context
代表的命名空间,另一个config
加上了前缀aop
,则代表它属于aop
代表的命名空间。这样就是的数据更加精确。
2. XML文档的验证
关键步骤如下:
- 使用DTD验证XML文档
- 使用Schema验证XML文档
2.1 使用DTD验证XML文档
DTD:Document Type Definition 记文档类型定义的缩写
合法的 XML 文档是“形式良好”的 XML 文档,同样遵守文档类型定义 (DTD) 的语法规则。
内部文档声明
内部DOCTYPE 声明:
假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中
<!DOCTYPE 根元素 [元素声明]>
带有 DTD 的 XML 文档实例:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ELEMENT note (to*,from?,heading,body+)>
<!ELEMENT to EMPTY>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (table|foot)>
<!ATTLIST to
address CDATA #REQUIRED
url CDATA #IMPLIED>
<!ATTLIST heading
message CDATA "Hello">
]>
<note>
<to address="NewYork" />
<to address="Beijing" />
<from>John</from>
<heading>Reminder</heading>
<body>
<foot>foot</foot>
</body>
</note>
- PCDATA(Parsed CDATA):可以是字符串、子元素、字符串和子元素
- CDATA(Character Data):是指字符串
- EMPTY:是指元素不能有内容,不能有尾标签
以上 DTD 解释如下:
!DOCTYPE note
(第二行)定义此文档是 note 类型的文档。!ELEMENT note (to*,from?,heading,body+)
(第三行)定义 note 元素有四个子元素:“to、from、heading,、body”to*
:to元素在父级元素note可以出现0次或者任意多次from?
:from元素在父级元素note只能出现0次或者1次heading
:heading元素在父级元素note下只能出现一次body+
:body元素在父级元素note下最少出现一次,可以出现多次
!ELEMENT to
(第四行)定义 to 元素为不能有内容,不能有尾标签,如:<to />
!ELEMENT from
(第五行)定义 from 元素为 “#PCDATA” 类型!ELEMENT heading
(第六行)定义 heading 元素为 “#PCDATA” 类型!ELEMENT body (table|foot)
(第七行)定义 body 元素下为table
和foot
,二者只能出现其一!ATTLIST to
定义to元素下的属性,分别为address
和url
#REQUIRED
:属性是必须的#IMPLIED
:属性不是必须的<!ATTLIST heading message CDATA "Hello">
:定义heading元素下的属性名为message,使用字符串赋值,默认值为:Hello
DTD元素中一些符号的用途如表所示:
符号 | 用途 | 示例 | 示例说明 |
---|---|---|---|
( ) | 用来给元素分组 | (古龙|金庸|梁羽生),(王朔|余杰),毛毛 | 表示分成三组 |
| | 在列出的对象中选择一个 | (男人|女人) | 表示男人或者女人必须出现,并且两者至少选其一 |
, | 对象必须按指定的顺序出现 | (西瓜,苹果,香蕉) | 表示西瓜、苹果、香蕉必须出现,并且按这个顺序出现 |
* | 该对象允许出现零次到任意多次(0或者多次) | (爱好*) | 爱好可以出现零次或多次 |
? | 该对象可以出现,但只能出现一次(0或1次) | (菜鸟?) | 菜鸟可以出现,也可以不出现,如果出现的话,最多只能出现一次 |
+ | 该对象最少出现一次,可以出现多次(1或多次) | (成员+) | 表示成员必须出现,而且可以出现多个成员 |
以下是属性类型的选项:
类型 | 描述 |
---|---|
CDATA | 值为字符数据 (character data) |
(en1|en2|…) | 此值是枚举列表中的一个值 |
ID | 值为唯一的 id |
IDREF | 值为另外一个元素的 id |
IDREFS | 值为其他 id 的列表 |
NMTOKEN | 值为合法的 XML 名称 |
NMTOKENS | 值为合法的 XML 名称的列表 |
ENTITY | 值是一个实体 |
ENTITIES | 值是一个实体列表 |
NOTATION | 此值是符号的名称 |
xml: | 值是一个预定义的 XML 值 |
默认值参数可使用下列值:
值 | 解释 |
---|---|
值 | 属性的默认值 |
#REQUIRED | 属性值是必需的 |
#IMPLIED | 属性不是必需的 |
#FIXED value | 属性值是固定的 |
外部文档声明
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:
<!DOCTYPE 根元素 SYSTEM "文件名">
这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to address="NewYork" />
<to address="Beijing" />
<from>John</from>
<heading>Reminder</heading>
<body>
<foot>foot</foot>
</body>
</note>
这是包含 DTD 的 “note.dtd” 文件:
<!DOCTYPE note [
<!ELEMENT note (to*,from?,heading,body+)>
<!ELEMENT to EMPTY>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (table|foot)>
<!ATTLIST to
address CDATA #REQUIRED
url CDATA #IMPLIED>
<!ATTLIST heading
message CDATA "Hello">
]>
2.2 使用Schema验证XML文档
XML Schema 是基于 XML 的 DTD 替代者。
XML Schema 描述 XML 文档的结构。
XML Schema 语言也称作 XML Schema 定义(XML Schema Definition,XSD)。
什么是 XML Schema?
XML Schema 的作用是定义 XML 文档的合法构建模块,类似 DTD。
XML Schema:
- 定义可出现在文档中的元素
- 定义可出现在文档中的属性
- 定义哪个元素是子元素
- 定义子元素的次序
- 定义子元素的数目
- 定义元素是否为空,或者是否可包含文本
- 定义元素和属性的数据类型
- 定义元素和属性的默认值以及固定值
XML Schema 是 DTD 的继任者
我们认为 XML Schema 很快会在大部分网络应用程序中取代 DTD。
理由如下:
- XML Schema 可针对未来的需求进行扩展
- XML Schema 更完善,功能更强大
- XML Schema 基于 XML 编写
- XML Schema 支持数据类型
- XML Schema 支持命名空间
一个简单的XML文档
请看这个名为 “note.xml” 的 XML 文档:
<?xml version="1.0" encoding="utf-8"?>
<note>
<to id="12" />
<to id="23" />
<from>John</from>
<heading>Reminder</heading>
<body>
<foot>foot</foot>
</body>
</note>
下面这个例子是一个名为 “note.xsd” 的 XML Schema 文件,它定义了上面那个 XML 文档的元素:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<!--定义元素to -->
<xsd:element name="to" >
<xsd:complexType>
<!--定义to元素的属性id 引用上面定义的id属性-->
<!--定义属性id 要求值是必须的 而且必须为正整数-->
<xsd:attribute name="id" use="required" type="xsd:positiveInteger"/>
</xsd:complexType>
</xsd:element>
<!--定义元素note 包含子元素 -->
<xsd:element name="note">
<xsd:complexType>
<xsd:sequence>
<!--定义note的子元素to 出现0次或者任意多次 -->
<xsd:element ref="to" maxOccurs="unbounded"
minOccurs="0" />
<!--定义note的子元素from 出现0次或者1次 -->
<xsd:element name="from" type="xsd:string" maxOccurs="1"
minOccurs="0" />
<!--定义note的子元素heading 只能出现一次 -->
<xsd:element name="heading" type="xsd:string" />
<!--定义note的子元素body 出现1次或者任意多次 -->
<!--定义元素body 包含子元素 -->
<xsd:element ref="body" maxOccurs="unbounded" minOccurs="1" >
<xsd:complexType>
<xsd:sequence>
<xsd:element name="foot" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<!--定义元素note 的id属性 引用定义的属性 -->
<xsd:attribute ref="id" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
对 XML Schema 的引用
此文件包含对 XML Schema 的引用:
<?xml version="1.0" encoding="UTF-8" ?>
<note xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="note.xsd" id="1">
<to id="12" />
<to id="23" />
<from>John</from>
<heading>Reminder</heading>
<body>
<foot>foot</foot>
</body>
</note>
XSD 元素
元素 | 解释 |
---|---|
all | 规定子元素能够以任意顺序出现,每个子元素可出现零次或一次。 |
annotation | annotation 元素是一个顶层元素,规定 schema 的注释。 |
any | 使创作者可以通过未被 schema 规定的元素来扩展 XML 文档。 |
anyAttribute | 使创作者可以通过未被 schema 规定的属性来扩展 XML 文档。 |
appInfo | 规定 annotation 元素中应用程序要使用的信息。 |
attribute | 定义一个属性。 |
attributeGroup | 定义在复杂类型定义中使用的属性组。 |
choice | 仅允许在 <choice> 声明中包含一个元素出现在包含元素中。 |
complexContent | 定义对复杂类型(包含混合内容或仅包含元素)的扩展或限制。 |
complexType | 定义复杂类型。 |
documentation | 定义 schema 中的文本注释。 |
element | 定义元素。 |
extension | 扩展已有的 simpleType 或 complexType 元素。 |
field | 规定 XPath 表达式,该表达式规定用于定义标识约束的值。 |
group | 定义在复杂类型定义中使用的元素组。 |
import | 向一个文档添加带有不同目标命名空间的多个 schema。 |
include | 向一个文档添加带有相同目标命名空间的多个 schema。 |
key | 指定属性或元素值(或一组值)必须是指定范围内的键。 |
keyref | 规定属性或元素值(或一组值)对应指定的 key 或 unique 元素的值。 |
list | 把简单类型定义为指定数据类型的值的一个列表。 |
notation | 描述 XML 文档中非 XML 数据的格式。 |
redefine | 重新定义从外部架构文件中获取的简单和复杂类型、组和属性组。 |
restriction | 定义对 simpleType、simpleContent 或 complexContent 的约束。 |
schema | 定义 schema 的根元素。 |
selector | 指定 XPath 表达式,该表达式为标识约束选择一组元素。 |
sequence | 要求子元素必须按顺序出现。每个子元素可出现 0 到任意次数。 |
simpleContent | 包含对 complexType 元素的扩展或限制且不包含任何元素。 |
simpleType | 定义一个简单类型,规定约束以及关于属性或仅含文本的元素的值的信息。 |
union | 定义多个 simpleType 定义的集合。 |
unique | 指定属性或元素值(或者属性或元素值的组合)在指定范围内必须是唯一的。 |
详细说明请参考官方手册:https://www.w3school.com.cn/schema/schema_elements_ref.asp
3. 使用DOM解析XML
关键步骤如下:
- 使用DOM读取XML数据
- 使用DOM添加XML数据
- 使用DOM修改XML数据
- 使用DOM删除XML数据
3.1 XML解析
在实际应用中,经常需要对XML文档进行各种操作。例如,在应用程序启动时读取XML配置文件信息,或者把数据库中的内容读取出来转换为XML文档形式,这是都会用到XML文档的解析技术。
目前常用的XML解析技术有4种:
-
DOM
DOM:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种方式。DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点)。其优缺点分别为:
优点:把XML文件在内存中构造树形结构,可以遍历和修改节点
缺点:如果文件比较大,内存有压力,解析的时间会比较长
-
SAX
SAX(simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。而且相比于DOM,SAX可以在解析文档的任意时刻停止解析。 其优缺点分别为:
优点: 解析可以立即开始,速度快,没有内存压力
缺点: 不能对节点做修改
-
DOM4J
非常优秀的Java XML API
性能优异、功能强大
开放源代码。官网:
https://dom4j.github.io/
3.2 使用DOM读取XML数据
1. DOM概念
DOM(Document Object Model)把XML文档映射成一个倒挂的树,如图所示:
Oracle公司提供了JAXP(Java API for XML Processing)来解析XML。JAXP会把XML文档转换成一个DOM树。JAXP包含3个包,这3个包都在JDK中:
- org.w3c.dom:W3C推荐的用于使用DOM解析XML文档的接口
- org.xml.sax:用于使用SAX解析XML文档的接口
- javax.xml.parsers:解析器工厂工具,通过该包下的类可以获得并配置特殊的分析器。如:通过调用
DocumentBuilder
类的parse(XML文件名)方法,可以得到解析XML文件的Document对象
DOM解析使用到的类都在这些包中,在使用DOM解析XML时需要导入这些相关的类。
2. 使用DOM读取手机收藏信息
使用DOM解析XML文件步骤如下:
- 创建解析器工厂对象,即:DocumentBuilderFactory对象
- 由解析器工厂对象创建解析器对象,即:DocumentBuilder对象
- 由解析器对象对指定的XML文件进行解析,构建对应的DOM树,创建Document对象
- 以Document对象为起点对DOM树的节点进行增加、删除、修改、查询等操作
下面通过示例使用DOM读取XML数据
手机收藏信息中的品牌和型号信息的XML文档代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<PhoneInfo>
<Brand name="华为">
<Type name="U8650"/>
<Type name="HW123"/>
<Type name="HW321"/>
</Brand>
<Brand name="苹果">
<Type name="iPhone4"/>
</Brand>
</PhoneInfo>
访问DOM树节点
-
显示
收藏信息.xml
文件中收藏的手机品牌和型号DOM解析XML文件步骤
1. 创建解析器工厂对象
2.解析器工厂对象创建解析器对象
3.解析器对象指定XML文件创建Document对象
4.以Document对象为起点操作DOM树
示例代码如下:
//DOM解析XML文件步骤
//创建解析器工厂对象
//1.创建解析器工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//2.解析器工厂对象创建解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
//3.解析器对象指定XML文件创建Document对象
Document doc = builder.parse(new File("src/收藏信息.xml"));
//4.以Document对象为起点操作DOM树
//获取所有brand节点列表信息
NodeList list = doc.getElementsByTagName("Brand");
System.out.println("手机\t型号");
for (int i = 0; i < list.getLength(); i++) {
Element element = (Element)list.item(i);
String name = element.getAttribute("name");
//获取所有Type节点列表信息
NodeList typeNodes = element.getElementsByTagName("Type");
for (int j = 0; j < typeNodes.getLength(); j++) {
Element type = (Element)typeNodes.item(j);
String typeName = type.getAttribute("name");
//获取元素的文本节点信息
//String text = type.getTextContent();
System.out.println(name+"\t"+typeName);
}
}
常用的接口以及接口中方法说明如下表所示:
常用接口 | 常用方法 | 说明 |
---|---|---|
Document:表示整个 XML 文档 | NodeList getElementsByTagName(String Tag) | 按文档顺序返回文档中指定标记名称的所有元素集合 |
Element createElement(String tagName) | 创建指定标记名称的元素 | |
Node:该文档树中的单个节点 | NodeList getChildNodes() | 获取该元素的所有子节点,返回节点集合 |
Element:XML 文档中的一个元素 | String getTagName() | 获取元素名称 |
保存XML文件
- 步骤如下:
- 获得TransformerFactory对象
- 创建Transformer对象
- 创建Document对象并添加节点信息
- 创建DOMSource对象 (包含XML信息)
- 设置输出属性(字符编码以及输出格式)
- 创建StreamResult对象(包含保存文件的信息)
- 将XML保存到指定文件中
示例代码如下:
//1. 获得TransformerFactory对象
TransformerFactory factory = TransformerFactory.newInstance();
//2. 创建Transformer对象
Transformer transformer = factory.newTransformer();
//3. 创建Document对象并添加节点信息
//3.1.创建解析器工厂对象
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
//3.2.解析器工厂对象创建解析器对象
DocumentBuilder builder = docFactory.newDocumentBuilder();
//3.3. 使用解析器对象创建Document对象
Document doc = builder.newDocument();
//3.4. 使用Document对象创建PhoneInfo节点
Element phoneInfo = doc.createElement("PhoneInfo");
//将PhoneInfo节点追加到文档的子节点中
doc.appendChild(phoneInfo);
//3.5. 使用Document对象创建Brand节点
Element huawei = doc.createElement("Brand");
//华为
huawei.setAttribute("name", "华为");
//huawei.setTextContent("设置元素内容");
//将Brand节点追加至PhoneInfo子节点中
phoneInfo.appendChild(huawei);
//3.6. 使用Document对象创建Type节点
Element type = doc.createElement("Type");
type.setAttribute("name", "U8650");
//将Type节点追加至华为的子节点中
huawei.appendChild(type);
type = doc.createElement("Type");
type.setAttribute("name", "HW123");
huawei.appendChild(type);
type = doc.createElement("Type");
type.setAttribute("name", "HW321");
huawei.appendChild(type);
//苹果
Element phone = doc.createElement("Brand");
phone.setAttribute("name", "苹果");
phoneInfo.appendChild(phone);
//创建子节点Type
type = doc.createElement("Type");
type.setAttribute("name", "iPhone4");
phone.appendChild(type);
//4. 创建DOMSource对象 (包含XML信息)
DOMSource source = new DOMSource(doc);
//5. 设置输出属性(字符编码、版本号以及输出格式)
transformer.setOutputProperty("encoding", "utf-8");
transformer.setOutputProperty("version", "1.0");
transformer.setOutputProperty("indent", "yes");
//6. 创建StreamResult对象(包含保存文件的信息)
StreamResult result = new StreamResult(new FileOutputStream("src/news.xml"));
//7. 将XML保存到指定文件中
transformer.transform(source, result);
System.out.println("写入成功");
添加DOM节点
给手机收藏信息XML中添加新的手机信息
-
添加新的Brand:三星
-
给Brand节点添加新的子标签Type:Note4
-
将Brand添加到DOM树中
实现步骤如下:
- 1.为XML文档构造DOM树
- 2.创建新节点,并设置子节点
- 3.将节点加到其所属父节点上
- 4.保存XML文档
示例代码如下:
//1.创建解析器工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//2.解析器工厂对象创建解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
//3.解析器对象指定XML文件创建Document对象
String fileName = "src/收藏信息.xml";
Document doc = builder.parse(new File(fileName));
//4.以Document对象为起点操作DOM树
//获取PhoneInfo节点
Element phoneInfo = (Element)doc.getElementsByTagName("PhoneInfo").item(0);
//创建Brand节点分别设置name属性
Element san = doc.createElement("Brand");
san.setAttribute("name", "三星");
//将其追加到PhoneInfo节点下
phoneInfo.appendChild(san);
//
Element type = doc.createElement("Type");
type.setAttribute("name", "note4");
san.appendChild(type);
//获得TransformerFactory对象
TransformerFactory transFactory = TransformerFactory.newInstance();
//创建Transformer对象
Transformer transformer = transFactory.newTransformer();
//设置格式化输出
transformer.setOutputProperty("indent", "yes");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new FileOutputStream(fileName));
transformer.transform(source, result);
System.out.println("添加成功!!");
修改DOM节点
修改手机收藏信息,需求如下:
- 给所有的Brand标签添加id属性,并分别赋值
- 将品牌信息为“华为”的节点属性修改为“HuaWei”
实现步骤如下:
- 1.为XML文档构造DOM树
- 2.找到符合修改条件的节点
- 3.为该节点增加新的属性id,并为id赋值
- 4.找到name属性的值为“华为”的节点,修改值为“HuaWei”
- 5.保存XML文档
示例代码如下:
//1.创建解析器工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//2.解析器工厂对象创建解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
//3.解析器对象指定XML文件创建Document对象
String fileName = "src/收藏信息.xml";
Document doc = builder.parse(new File(fileName));
//4.以Document对象为起点操作DOM树
//获取所有brand节点列表信息
NodeList phoneInfo = doc.getElementsByTagName("Brand");
for (int i = 0; i < phoneInfo.getLength(); i++) {
Element node = (Element)phoneInfo.item(i);
//增加id属性
node.setAttribute("id", i+1+"");
if(node.getAttribute("name").equals("华为")) {
//为name属性设置值
node.setAttribute("name", "HuaWei");
}
}
//获得TransformerFactory对象
TransformerFactory transFactory = TransformerFactory.newInstance();
//创建Transformer对象
Transformer transformer = transFactory.newTransformer();
//设置格式化输出
transformer.setOutputProperty("indent", "yes");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new FileOutputStream(fileName));
transformer.transform(source, result);
System.out.println("修改成功!!");
删除DOM节点
删除手机收藏信息,需求如下:
- 删除手机品牌信息“三星”
实现步骤如下:
- 1.为XML文档构造DOM树
- 2.找到符合删除条件的节点
- 3.调用该节点的方法获取父节点,通过父节点删除子节点
- 4.保存XML文档
示例代码如下:
//1.创建解析器工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//2.解析器工厂对象创建解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
//3.解析器对象指定XML文件创建Document对象
String fileName = "src/收藏信息.xml";
Document doc = builder.parse(new File(fileName));
//4.以Document对象为起点操作DOM树
//获取所有brand节点列表信息
NodeList phoneInfo = doc.getElementsByTagName("Brand");
for (int i = 0; i < phoneInfo.getLength(); i++) {
Element node = (Element)phoneInfo.item(i);
if(node.getAttribute("name").equals("三星")) {
//删除 找到该节点的父节点实现其子节点的删除功能
node.getParentNode().removeChild(node);
}
}
//获得TransformerFactory对象
TransformerFactory transFactory = TransformerFactory.newInstance();
//创建Transformer对象
Transformer transformer = transFactory.newTransformer();
//设置格式化输出
transformer.setOutputProperty("indent", "yes");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new FileOutputStream(fileName));
transformer.transform(source, result);
System.out.println("删除成功!!");
4. 使用 DOM4J 解析XML
关键步骤如下:
- 了解DOM4J的相关接口
- 使用DOM4J读取、添加、修改、删除XML数据
补充知识:
DOM4J是目前使用非常广泛的一种解析XML的技术,与DOM相比,其使用灵活,操作简单,学习时要灵活理解几个重要接口的用法。DOM4J官网:https://dom4j.github.io/
4.1 DOM4J解析
DOM4J是 dom4j.org 出品的一个开源 XML 解析包。DOM4J应用于 Java 平台,采用了 Java 集合框架并完全支持 DOM,SAX 和 JAXP。
DOM4J 使用起来非常简单。只要你了解基本的 XML-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 表达式 |
4.2 使用DOM4J操作XML
1. 读取XML文档
使用DOM4J读取手机收藏信息
实现步骤如下:
- 1.导入DOM4J的jar包
- 2.指定要解析的XML文件
- 3.把XML文件转换成Document对象
- 4.获取节点属性或文本的值
关键代码如下:
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("src/news.xml"));
// 获取XML的根节点
Element root = doc.getRootElement();
// 遍历所有的Brand标签
for(Iterator<Element> its = root.elementIterator();its.hasNext();) {
Element item = its.next();
//Attribute attr = item.attribute("name");
// 输出标签的name属性
System.out.println("手机品牌是:"+item.attributeValue("name"));
// 遍历Type标签
for(Iterator<Element> types = item.elementIterator();types.hasNext();) {
Element type = types.next();
//获取元素内容
//System.out.println(type.getText());
//Attribute at = type.attribute("name");
// 输出标签的name属性
System.out.println("型号是:"+type.attributeValue("name"));
}
}
2. 保存XML文档
使用DOM4J保存手机收藏信息
实现步骤如下:
- 1.导入DOM4J的jar包
- 2.使用DocumentHelper创建Document对象
- 3.创建根节点
- 4.创建子节点并添加属性值
关键代码如下:
//使用DocumentHelper创建Document对象
Document document = DocumentHelper.createDocument();
//创建根节点
Element phoneInfo = document.addElement("PhoneInfo");
//华为
Element brand = phoneInfo.addElement("Brand").addAttribute("name", "华为");
brand.addElement("Type").addAttribute("name", "U8650");
brand.addElement("Type").addAttribute("name", "HW123");
brand.addElement("Type").addAttribute("name", "HW321");
//苹果
Element phone = phoneInfo.addElement("Brand").addAttribute("name", "苹果");
phone.addElement("Type").addAttribute("name", "iPhone4");
phone.addElement("Type").addAttribute("name", "iPhone12").setText("元素内容");
//三星
Element san = phoneInfo.addElement("Brand").addAttribute("name", "三星");
san.addElement("Type").addAttribute("name", "note4");
san.addElement("Type").addAttribute("name", "note5").setText("元素内容");
//设置输出格式
OutputFormat format = OutputFormat.createPrettyPrint();
FileWriter out = new FileWriter("foo.xml");
XMLWriter writer = new XMLWriter(out, format);
writer.write(document);
out.close();
System.out.println("写入成功!!");
3. 修改XML文档
-
修改手机收藏信息,需求如下:
- 给所有的Brand标签添加id属性,并分别赋值
- 将品牌信息为“华为”的节点属性修改为“HuaWei”
实现步骤如下:
- 1.导入DOM4J的jar包
- 2.指定要解析的XML文件
- 3.把XML文件转换成Document对象
- 4.修改子节点属性
关键代码如下:
SAXReader reader = new SAXReader();
String path = "src/news.xml";
Document document = reader.read(new File(path));
Element root = document.getRootElement();
int id = 0;
//遍历子节点
for (Iterator<Element> it = root.elementIterator(); it.hasNext();) {
Element element = it.next();
id++;
element.addAttribute("id", id+"");
if(element.attribute("name").getValue().equals("华为")) {
element.addAttribute("name", "huawei");
//element.addText("设置元素内容");
}
}
//设置输出格式
OutputFormat format = OutputFormat.createPrettyPrint();
FileWriter out = new FileWriter(path);
XMLWriter writer = new XMLWriter(out, format);
writer.write(document);
out.close();
System.out.println("修改成功!!");
4. 删除XML文档
删除手机收藏信息,需求如下:
- 删除手机品牌信息“三星”
实现步骤如下:
- 1.导入DOM4J的jar包
- 2.指定要解析的XML文件
- 3.把XML文件转换成Document对象
- 4.获取根节点
- 5.遍历子节点,找到符合条件的子节点
- 6.调用该节点的方法获取父节点,通过父节点删除子节点
代码如下:
SAXReader reader = new SAXReader();
String path = "src/news.xml";
Document document = reader.read(new File(path));
Element root = document.getRootElement();
for(Iterator<Element> its = root.elementIterator();its.hasNext();) {
Element brand = its.next();
if("三星".equals(brand.attributeValue("name"))) {
brand.getParent().remove(brand);
}
}
//设置输出格式
OutputFormat format = OutputFormat.createPrettyPrint();
FileWriter writer = new FileWriter(path);
XMLWriter xml = new XMLWriter(writer,format);
xml.write(document);
writer.close();
System.out.println("删除成功!!");