1. XML详解
1.1. XML介绍
1.1.1. 什么是XML
XML的全称为eXtensible Markup Language,译为可扩展标记语言。XML语法上和HTML比较相似,但HTML中的元素是固定的,而XML的标签是可以由用户自定义的。
W3C在1998年2月发布1.0版本,2004年2月又发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。
1.1.2. 百度百科
xml 即可扩展标记语言。可扩展标记语言,标准通用标记语言的子集,一种用于标记电子文件使其具有结构性的标记语言。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。
什么是可扩展标记语言?
- 可扩展标记语言是一种很像超文本标记语言的标记语言。
- 它的设计宗旨是传输数据,而不是显示数据。
- 它的标签没有被预定义。您需要自行定义标签。
- 它被设计为具有自我描述性。
- 它是W3C的推荐标准。
1.1.3. 维基百科
可扩展标记语言(英语:eXtensible Markup Language,简称: XML),是一种标记语言。标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。如何定义这些标记,既可以选择国际通用的标记语言,比如HTML,也可以使用像XML这样由相关人士自由决定的标记语言,这就是语言的可扩展性。XML是从标准通用标记语言(SGML)中简化修改出来的。它主要用到的有可扩展标记语言、可扩展样式语言(XSL)、XBRL和XPath等。
1.1.4. 发展历史
XML是从1995年开始有其雏形,并向W3C(万维网联盟)提案,而在1998二月发布为W3C的标准(XML1.0)。XML的前身是SGML(The Standard Generalized Markup Language),是自IBM从1960年代就开始发展的GML(Generalized Markup Language)标准化后的名称。
GML的重要概念:
- 文件中能够明确的将标示与内容分开
- 所有文件的标示使用方法均一致
1978年,ANSI将GML加以整理规范,发布成为SGML,1986年起为ISO所采用(ISO 8879),并且被广泛地运用在各种大型的文件计划中,但是SGML是一种非常严谨的文件描述法,导致过于庞大复杂(标准手册就有500多页),难以理解和学习,进而影响其推广与应用。
同时W3C也发现到HTML的问题:
- 不能解决所有解释数据的问题 - 像是影音档或化学公式、音乐符号等其他形态的内容。
- 性能问题 - 需要下载整份文件,才能开始对文件做搜索。
- 扩充性、弹性、易读性均不佳。
为了解决以上问题,专家们使用SGML精简制作,并依照HTML的发展经验,产生出一套使用上规则严谨,但是简单的描述数据语言:XML。
1.1.5. 用途
- 保存关系型数据
<?xml version="1.0" encoding="UTF-8"?> <person> <name>baidu</name> <age>15</age> </person>
- 配置文件
<?xml version="1.0" encoding="UTF-8"?> <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>app.servlet.MyServlet</servlet-class> </servlet>
1.2. 第一个XML文件
1.2.1. XML文档声明
- 文档声明必须为<?xml开头,以?>结束;
- 文档声明必须从文档的行0列位置开始; l 文档声明只有三个属性:
- versioin:指定XML文档版本。必须属性,因为我们不会选择1.1,只会选择1.0;
- encoding:指定当前文档的编码。可选属性,默认值是utf-8;
- standalone:指定文档独立性。可选属性,默认值为yes,表示当前文档是独立文档。如果为no表示当前文档不是独立的文档,会依赖外部文件。
1.2.2. 元素
元素是XML文档中最重要的组成部分:
- 普通元素的结构:开始标签、元素体、结束标签,例如:<hello>大家好</hello>;
- 开始标签和结束标签:
- 标签名由字母、数字、下划线(_)、中划线(-)、冒号(:)和点号(.)组成,但不能以数字、中划线和点号开始。
- 标签名不能包含空格。
- 标签名中尽量不要出现英文冒号“:”。
- 标签名不能以字符“xml”开始。
- XML命名区分大小写,例如<a>和<A>是两个不同的元素。
- 元素体:元素体可以是元素,也可以是文本,例如:<b><a>你好</a></b>,其中<b>元素的元素体是<a>元素,而<a>元素的元素体是文本;
l 空元素:空元素只有开始标签,而没有结束标签。
- 开始标签和结束标签:
- <c/>,当元素必须自己闭合。
- <c></c>,只由开始标签和结束标签组成
1.2.3. 属性
<?xml version="1.0" encoding="UTF-8"?> <person id="001"> <name>baidu</name> <age>15</age> </person>
- 属性是元素的一部分,它必须出现在元素的开始标签中;
- 属性的定义格式:属性名=属性值,其中属性值必须使用英文的单引或双引;
- 一个元素可以有0 - N个属性,但一个元素中不能出现同名属性;
1.2.4. 注释
XML的注释与HTML相同,即以“<!--”开始,以“-->”结束。注释内容会被XML解析器忽略!
<!-- 第一行不能写注释 --> <?xml version="1.0" encoding="UTF-8"?> <!-- 这里是注释 --> <!-- 注释也可以这样来书写 --> <!-- 注释中不能包含"--" 注释不能以"--->"结束 --> <person <!-- 注释不要放在这里 --> > </person>
注意:
- 不要把注释放在标签内,这样的XML格式不够好。
- 不要把注释放在XML声明之前。
- 不要在注释中使用“--”。
- XML注释不能以“--->”结尾。
1.2.5. 转义字符
XML中的转义字符与HTML一样。
因为很多符号已经被XML文档结构所使用,所以在元素体或属性值中想使用这些符号就必须使用转义字符,例如:“<”、“>”、“’”、“””、“&”。
例如:<a><hello></a>,<a>元素内部会被解释为:<hello>
1.2.6. CDATA段
当大量的转义字符出现在xml文档中时,会使xml文档的可读性大幅度降低。这时如果使用CDATA段就会好一些。
在CDATA段中出现的“<”、“>”、“””、“’”、“&”,都无需使用转义字符。这可以提高xml文档的可读性。
<a><![CDATA[<a>]]></a>
注意:在CDATA段中不能包含“]]>”,即CDATA段的结束定界符。
1.2.7. 处理指令
处理指令,简称PI(Processing instruction)。处理指令用来指挥解析器如何解析XML文档内容。
例如,在XML文档中可以使用xml-stylesheet指令,通知XML解析器,应用css文件显示xml文档内容。
<?xml-stylesheet type="text/css" href="a.css"?>
处理指令以“<?”开头,以“?>”结束,这一点与xml文档声明相同。
示例:
a.css文件:
gj1 {font-size: 200px; color: red;} gj2 {font-size: 100px; color: green;} gj3 {font-size: 10px;} gj4 {font-size: 50px; color: blue;}
xml文件:
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/css" href="a.css" ?> <gjm> <gj1>中国</gj1> <gj2>美国</gj2> <gj3>日本</gj3> <gj4>英国</gj4> </gjm>
1.2.8. 格式良好的XML文档
格式良好的XML就是格式正确的XML文档,只有XML的格式是良好的,XML解释器才能解释它。下面是对格式良好XML文档的要求:
- 必须要有XML文档声明;
- 必须且仅能有一个根元素;
- 元素和属性的命名必须遵循XML要求;
- 元素之间必须合理包含,例如:<a><b>xxx</b></a>是合理的,而<a><b>xxx</a></b>就是错误的包含。
2. DTD详解
2.1. DTD介绍
2.1.1. 什么是DTD
DTD的全称为Document Type Definition,译为文档类型定义,用来约束XML文档。
可以把DTD理解为创建XML文档的结构!例如可以用DTD要求XML文档的根元素名为<students>,<students>中可以有1~N个<student>,<student>子元素为<name>、<age>和<sex>,<student>元素还有number属性。DTD不是XML文档,它是XML文档的约束文件!就像法律与人一样!
2.1.2. 百度百科
文档类型定义是一套关于标记符的语法规则。它是标准通用标记语言和[1]可扩展标记语言1.0版规格的一部分,是文档的验证机制。文档类型定义是一种保证标准通用标记语言、可扩展标记语言文档格式正确的有效方法,可通过比较文档和文档类型定义文件来看文档是否符合规范,元素和标签使用是否正确。文件实例提供应用程序一个数据交换的格式。在文档类型定义正是让标准通用标记语言、可扩展标记语言文件能成为数据交换标准,因为不同的公司只需定义好标准文档类型定义,各公司都能依文档类型定义建立文档实例,并且进行验证,如此就可以轻易的建立标准和交换数据,这样满足了网络共享和数据交互。文档类型定义文件是一个美国信息交换标准代码文本文件。
2.1.3. 维基百科
XML 文件的文件型别定义(Document Type Definition)可以看成一个或者多个 XML 文件的模板,在这里可以定义 XML 文件中的元素、元素的属性、元素的排列方式、元素包含的内容等等。
DTD(Document Type Definition)概念缘于 SGML,每一份 SGML 文件,均应有相对应的 DTD。对 XML 文件而言,DTD 并非特别需要,well-formed XML 就不需要有 DTD。DTD 有四个组成如下:
- 元素(Elements)
- 属性(Attribute)
- 实体(Entities)
- 注释(Comments)
由于 DTD 限制较多,使用时较不方便,近来已渐被 XML Schema 所取代。
2.2. 引入DTD
2.2.1. 内部DTD
所谓的内部DTD是指将DTD与XML数据定义放在同一份文档中。内部DTD紧跟在XML声明和处理指令之后,以“<!DOCTYPE”开始,以“>”结束。其语法格式如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 根元素名[ 元素描述 ]> XML文档主体部分
示例:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!DOCTYPE person [ <!ELEMENT person (name, age)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)> ]> <person> <name>baidu</name> <age>15</age> </person>
2.2.2. 外部DTD
XML文档既可以使用内部DTD,也可以引用外部的DTD。外部DTD的好处是一个DTD文件可以方便地被多个XML文档共享。引入外部DTD的语法格式如下:
<!DOCTYPE 根元素名 SYSTEM "外部DTD的URI">
示例:
DTD文件
<!ELEMENT person (name, age)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)>
XML文件
<?xml version="1.0" encoding="utf-8" standalone="no" ?> <!DOCTYPE person SYSTEM "person.dtd"> <person> <name>baidu</name> <age>15</age> </person>
2.2.3. 公用DTD
有一种外部DTD是由某个权威机构制定,供特定行业或公众使用,这种DTD又被称为公用DTD。引用公用DTD的格式如下:
<!DOCTYPE 根元素 PUBLIC "DTD的标识名" "公用的DTD的URI">
2.3. 定义元素
2.3.1. 定义元素语法
定义元素的语法:<!ELEMENT 元素名 元素描述>
- <!ELEMENT person (#PCDATA)>,定义名为person的元素,内容为文本类型。
- <!ELEMENT person (name,age)>,定义名为person元素,内容依次为name、age元素。
- <!ELEMENT person ANY>,定义名为person元素,内容任意。
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!DOCTYPE person [ <!ELEMENT person ANY> ]> <person> <name>baidu</name> <age>15</age> </person>
- <!ELEMENT person EMPTY>,定义名为student元素,不能有内容,即空元素,注意空元素是可以有属性的。
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!DOCTYPE person [ <!ELEMENT person EMPTY> ]> <person />
2.3.2. 有序的子元素
如果使用英文逗号(,)作为子元素之间的分隔符,那么子元素之间必须遵守所定义的顺序。
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!DOCTYPE person [ <!ELEMENT person (name, qq)> ]> <person> <name>baidu</name> <age>15</age> </person>
2.3.3. 互斥的子元素
互斥的子元素表示一系列子元素之间只能出现其中之一。互斥子元素之间使用竖线(|)分隔。
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!DOCTYPE person [ <!ELEMENT person (name | age )> ]> <person> <name>baidu</name> </person>
2.3.4. 子元素出现次数
子元素的出现次数通过在元素声明后紧跟一个表示次数的特殊标记来表示,DTD中表示次数的特殊标记有以下三种:
- +:表示子元素可以出现1次或多次。
- *:表示子元素可以出现0次或多次。
- ?:表示子元素可以出现0次或1次。
2.3.5. 无序的子元素
理论上,DTD没有专门为定义无序的子元素提供语法。如果希望使用DTD来表示某个元素之内接收无序的子元素,怎么办呢?可以利用如下方式实现:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!DOCTYPE person [ <!ELEMENT person (name | age )+> ]> <person> <age>15</age> <name>baidu</name> </person>
2.4. 定义属性
2.4.1. 定义属性语法
元素除了可以包含子元素之外,还可以添加属性,为XML元素添加属性的格式如下:
<person> <name>baidu</name> </person>
DTD可以定义属性所属的元素、属性类型和属性必须遵守的规则等,DTD中定义属性的语法格式如下:
<!ATTLIST 元素名 属性名 属性类型 [设置说明]>
2.4.2. 定义属性规则
在DTD中可以使用多个<!ATTLIST>定义属性,元素对属性的约束规则有如下几种:
- #REQUIRED:必需的属性,意味着必须为该元素提供该属性。
- #IMPLIED:该属性是可有可无的。
- #FIXED:该属性的值是固定的,定义时必须指定固定值。
2.4.3. 定义属性类型
DTD支持的属性类型:
- CDATA:该属性只能是字符串数据。
- en1|en2|en3:该属性是一系列枚举值之一。
- ID:该属性值必须是有效的标识符,且该属性值可用于标识该元素,因而该属性值在此XML文档中必须唯一。
- IDREF:该属性值必须引用自另一个已有的ID属性值。
- IDREFS:该属性值必须引用自多个已有的ID属性值,多个ID属性值之间用空格隔开。
- NMTOKEN:该属性值必须是一个合法的XML名称,它也指定了该属性值是字符串数据,但比CDATA具有更强的约束。
- NMTOKENS:该属性值可以是多个NMTOKEN,多个NMTOKEN之间用空格隔开。
- ENTITY:该属性值是一个外部实体,例如图片文件等。
- ENTITIES:该属性值是多个外部实体,多个实体之间以空格隔开。
- NOTATION:该属性值是在DTD中声明过的符号(NOTATION)。过期规范,尽量避免使用。
- xml:该属性值是一个预定义的XML值。
2.5. 定义实体
2.5.1. 什么是实体
有时在XML中会出现很多相同的内容,例如“原来我就是麦兜的QQ空间”,这个名称太长了,我们希望把这个值与一个“符号”绑定,然后在需要这个名称时使用它绑定的“符号”即可。这个符号就是实体了。例如:“&麦兜;”!
其中“麦兜”是实体名,而“原来我就是麦兜的QQ空间”是实体值,XML被解析时,所有实体会被替换成实体名。
2.5.2. 实体分类
实体一般分为两种:一般实体和参数实体。
- 一般实体:在XML文档中使用;
- 参数实体:在DTD使用。
2.5.3. 一般实体
一般实体是XML文档里使用的实体,定义普通实体的语法格式如下:
<!ENTTITY 实体名 "实体值">
使用一般实体的语法格式如下:
&实体名;
示例:
DTD文件:
<!ELEMENT person (name, qq, weixin)> <!ELEMENT name (#PCDATA)> <!ELEMENT qq (#PCDATA)> <!ELEMENT weixin (personal, public)> <!ELEMENT personal (#PCDATA)> <!ELEMENT public (#PCDATA)> <!ENTITY jyl "原来我就是麦兜">
XML文件:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE person SYSTEM "08_person.dtd"> <person> <name>&baidu;</name> <age>15</age> </person>
2.5.4. 参数实体
还有一种实体只能在DTD中使用,这种实体被称为参数实体,定义和使用参数实体与普通实体有小小的差别。定义参数实体的语法格式如下:
<!ENTITY % 实体名 "实体值">
在DTD中引用参数实体的语法格式如下:
%实体名;
示例:
DTD文件:
<!ELEMENT person (name, age)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)><!ELEMENT personal (#PCDATA)> <!ELEMENT public (#PCDATA)> <!ENTITY baidu "15">
XML文件:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE person SYSTEM "08_person.dtd"> <person> <name>&baidu;</name> <age>15</age> </person>
3. Schema详解
3.1. Schema介绍
3.1.1. 什么是Schema
XML Schema是DTD的替代者,它采用标准XML语法来定义XML文档语义约束,不仅可以定义XML文档的结构,还可以定义XML文档内容约束。XML Schema也被称为XML Schema定义(XML Schema Definition,XSD)。
3.1.2. 百度百科
它的作用是定义一份XML文档的合法组件群,就像文档类型定义(外语缩写:DTD)的作用一样,一份XML Schema定义了:
- 可以出现在文档里的元素;
- 可以出现在文档里的属性;
- 哪些元素是子元素;
- 子元素的顺序;
- 子元素的数量;
- 一个元素应是否能包含文本,或应该是空的;
- 元素和属性的数据类型;
- 元素和属性的默认值和固定值。
3.1.3. 维基百科
XML Schema因为W3C的推荐,在2001年五月发布,是许多XML schema languages中的一支。它是首先分离于XML本身的纲要语言,故取得W3C的推荐地位。像所有XML纲要语言一样,XML Schema有时用来表达一组纲要──一组XML文件必须遵守的规定,这样根据该纲要才‘合法(Valid)’。然而,不像大部分其他纲要语言一样,XML Schema亦意图设计来确认在一收集来的资料与内附特殊资料型别的结果,它对开发XML文件处理软件有助益,不过同时召来了非议。
3.2. 如何使用Schema
3.2.1. 一个简单的Schema
<?xml version="1.0" encoding="utf-8" ?> <person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ① xmlns="http://www.example.org/person" ② xsi:schemaLocation="http://www.example.org/person person.xsd" ③> <name>baidu</name> <age>15</age> </person>
上述XML文档中,在person标签中多了一些信息,这些信息代表什么含义呢?
① 号代码导入了http://www.w3.org/2001/XMLSchema-instance命名空间对应的XML Schema。
② 号代码指定了使用http://www.longestory.com/schema命名空间的XML Schema所定义的Schema组件。
③ 号代码指定了有命名空间的XML Schema的命名空间及其URI。
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/01_person" xmlns:tns="http://www.example.org/01_person" elementFormDefault="qualified"> <element name="person"> <complexType> <sequence> <element name="name" type="string"></element> <element name="age" type="string"></element> </sequence> </complexType> </element> </schema>
上述Schema文件中,各个标签分别代表什么含义呢?
- element标签:用于表示XML文件中包含的元素,在element标签的name属性值指定。
- 复杂元素:含有子元素的元素,类型使用<complexType>标签指定。
- 简单元素:不含有子元素的元素,类型使用element标签的type属性值指定。
- sequence元素:用于表示指定元素中,含有哪些元素内容。
3.2.2. Schema的数据类型
Schema与DTD最大的不同之处就在于,Schema提供了强大的数据类型支持,不管是定义XML元素还是定义XML属性,都需要指定数据类型。
Schema提供的简单类型大致可分为内建类型和自定义类型,我们只需要记住一点:内建类型可以直接拿来定义元素或属性的数据类型。
Schema的数据类型相当庞大和繁琐,这里我们不做过多学习。实际上,掌握Schema的数据类型内容,就至少掌握了Schema 70%的内容了。
3.2.3. Schema内置类型
- 字符串及相关类型
- string:它会原封不动地保留字符串内容前面、后面及中间的所用空白。
- normalizedString:它会将字符串内容中包含的换行、制表符和回车符都替换成空白。
- token:它会将字符串内容中包含换行、制表符和回车符都替换成空白,并自动删除字符串前后的空白。如果字符串中间包含多个连续的空白,多个连续的空白会被压缩成单个空白。
- 数值类型
- 浮点数:主要有float和double。
- 精确小数:decimal。
- 整数:integer。
- 日期与时间类型
- Boolean类型
Boolean数据类型只能接收true、false、0和1等4个值。
- anyURI类型
anyURI类型的值必须是一个合法的URI,URI是一个比URL覆盖范围更广的概念。
- 二进制
XML Schema提供了2个二进制类型:hexBinary和base64Binary。
3.3. 合并多个Schema
3.3.1. 使用include元素
<include/>元素可以将另一份Schema包含到当前Schema中,使用<include/>元素包含一个Schema有如下几个要求:
-
- <include/>元素必须作为Schema的根元素<schema/>的子元素。
- <include/>元素必须在<shcema/>元素的开头,只有<import/>、<redefine/>和<annotation/>元素可放在<include/>元素之前。
- 使用<include/>元素包含的Schema要么没有目标命名空间,要么其目标命名空间与当前Schema的目标命名空间相同。
<include/>元素的使用非常简单,只需指定如下两个属性即可:
-
- id:指定该<include/>元素的唯一标识,通常无须指定。
- schemaLocation:指定<include/>元素包含的Schema的位置,该属性值既可以是相对位置,也可以是绝对位置。这是个必填属性。
included.xsd
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <simpleType name="ageType"> <restriction base="int"> <maxExclusive value="100" /> <minExclusive value="0" /> </restriction> </simpleType> </schema>
include.xsd
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org" xmlns:tns="http://www.example.org" elementFormDefault="qualified" attributeFormDefault="unqualified"> <include schemaLocation="02_included.xsd" /> <element name="age" type="tns:ageType" /> </schema>
3.3.2. 使用redefine元素
基本上,可以把<redefine/>元素当成<include/>元素的增强版,它完全可以取代<include/>元素。但<redefine/>元素允许当前Schema重定义被包含Schema里的Schema组件,包括重定义类型、重定义元素组和重定义属性组等。
- 重定义的组件必须是被<redefine/>包含进来的Schema里已有的组件。
- 重定义的组件只能基于被包含Schema里已有的组件增加限制或增加扩展。
- 如果是采用增加限制的方式来重定义原有的组件,则<redefine/>元素里所包含的约束不能违反原类型里已有的约束。
redefined.xsd
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <simpleType name="ageType"> <restriction base="int"> <maxExclusive value="100" /> <minExclusive value="0" /> </restriction> </simpleType> </schema>
redefine.xsd
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/03_redefine" xmlns:tns="http://www.example.org/03_redefine" elementFormDefault="qualified"> <redefine schemaLocation="03_redefined.xsd"> <simpleType name="ageType"> <restriction base="tns:ageType"> <maxExclusive value="80" /> </restriction> </simpleType> </redefine> <element name="age" type="tns:ageType" /> </schema>
3.3.3. 使用import元素
<import/>元素可以将另一份Schema包含到当前Schema中,使用<import/>元素包含一个Schema有如下几个要求:
- <import/>元素必须作为Schema的根元素<schema/>的子元素。
- <import/>元素必须在<shcema/>元素的开头,只有<include/>、<redefine/>和<annotation/>元素可放在<import/>元素之前。
- 使用<import/>元素包含的Schema要么没有目标命名空间,要么其目标命名空间与当前Schema的目标命名空间相同。
<import/>元素的使用非常简单,只需指定如下两个属性即可:
- id:指定该<import/>元素的唯一标识,通常无须指定。
- schemaLocation:指定<import/>元素包含的Schema的位置,该属性值既可以是相对位置,也可以是绝对位置。这是个必填属性。
- namespace:指定被导入Schema的目标命名空间。
imported.xsd
<?xml version="1.0" encoding="UTF-8"?> <xsi:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org" xmlns="http://www.example.org" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsi:simpleType name="ageType"> <xsi:restriction base="xsi:int"> <xsi:maxExclusive value="100" /> <xsi:minExclusive value="0" /> </xsi:restriction> </xsi:simpleType> </xsi:schema>
import.xsd
<?xml version="1.0" encoding="UTF-8"?> <xsi:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/04_import" xmlns="http://www.example.org/04_import" xmlns:import="http://www.example.org" elementFormDefault="qualified"> <xsi:import schemaLocation="04_imported.xsd" namespace="http://www.example.org"></xsi:import> <xsi:element name="age" type="import:ageType" /> </xsi:schema>
3.4. Schema命名空间
3.4.1. 什么是命名空间
如果一个XML文档中使用多个Schema文件,而这些Schema文件中定义了相同名称的元素时就会出现名字冲突。这就像一个Java文件中使用了import java.util.*和import java.sql.*时,在使用Date类时,那么就不明确Date是哪个包下的Date了。
总之命名空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的命名空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。
3.4.2. Schema的命名空间
在定义一份Schema文件时可以在根元素中指定targetNamespace属性,指定该Schema定义的元素、属性和类型等Schema组件放在哪个命名空间下。
<?xml version="1.0" encoding="UTF-8"?> <!-- schema标签: * targetNamespace:指定该Schema文件的命名空间,类似于Java类的包结构 * xmlns:指定该命名空间下,使用任何元素、属性和类型前没有任何前缀 * xmlns:tns:指定该命名空间下,使用任何元素、属性和类型前添加前缀tns --> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org" xmlns:tns="http://www.example.org" elementFormDefault="qualified"> <element name="person" type="string"></element> </schema>
上面的Schema文件中,指定了targetNamespace="http://www.example.org" ,这表明了Schema定义的所有组件(包括元素、属性和类型等)都位于这个命名空间下。
上面的Schema文件中,指定了xmlns="http://www.w3.org/2001/XMLSchema" ,这表明了该命名空间的前缀为空字符串。
上面的Schema文件中,指定了xmlns:tns="http://www.example.org" ,这表明了该命名空间的前缀为“tns”。
3.4.3. XML指定XSD文件
在定义一份XML文件时可以在根元素中指定schemaLocation属性,指定引入哪个Schema文件。格式如下:
<根元素 schemaLocation="目标命名空间 xsd文件路径"></根元素>
例如:
<?xml version="1.0" encoding="utf-8" ?> <person xmlns:xsi="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org" xsi:schemaLocation="http://www.example.org namespace.xsd"> </person>
上面的XML文件中,指定xsi:schemaLocation="http://www.example.org namespace.xsd" ,表明该XML文件使用http://www.example.org 命名空间下的namespace.xsd 的Schema约束文件。
而由于schemaLocation属性是由W3C组织提供的,所以在使用schemaLocation属性之前,需要引入W3C的命名空间xmlns:xsi="http://www.w3.org/2001/XMLSchema" 。
上面的XML文件中,指定xmlns="http://www.example.org" ,表明使用Schema文件的命名空间为默认的(该命名空间下的元素、属性和类型在使用,不用使用前缀)。
如果在一个XML文件需要指定多个命名空间的Schema文件时,可以这样来指定:
<根元素 schemaLocation="目标命名空间1 xsd文件路径1 目标命名空间2 xsd文件路径2"> </根元素>
例如:
<?xml version="1.0" encoding="utf-8" ?> <person xmlns:xsi="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org http://www.king.org " xsi:schemaLocation="http://www.example.org namespace.xsd http://www.king.org person.xsd"> </person>
3.4.4. 定义命名空间
在一个XML文件中,是可以指定多个命名空间下的Schema文件的。但是,现在如果来定义一个元素的话,我们并不知道该元素是位于哪个命名空间下的Schema文件。
<?xml version="1.0" encoding="utf-8" ?> <beans xsi:schemaLocation="http://www.springframework.org/schema/beans spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean></bean> </beans>
例如上面的例子一样,我们只使用schemaLocation 属性来指定引入了哪些Schema文件是不够的。schemaLocation属性的作用就相当于Java中导入Jar包一样,最终还是需要在Java中使用import来指定具体包名的。而xmlns属性的作用就是相当于Java中的import命令一样,来指定具体使用了哪个命名空间下Schema文件。
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns:b="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <b:bean></b:bean> <aop:scoped-proxy/> </beans>
上面的XML文件中使用xmlns:b="http://www.springframework.org/schema/beans" 指定使用该命名空间下的元素、属性和类型时,需要在其前面增加“b”前缀,例如<b:bean></b:bean>。而xmlns:aop="http://www.springframework.org/schema/aop"指定使用该命名空间下的元素、属性和类型时,需要在其前面增加“aop”前缀,例如<aop:scoped-proxy/>。
3.4.5. 默认命名空间
如果在一个XML文件中,大量地使用同一个命名空间下的Schema文件定义的元素、属性和类型的话,每一个元素、属性和类型在定义时,都需要增加前缀。而这种做法显然很麻烦,所以我们还可以将这样的Schema文件的命名空间设置为默认命名空间。这种默认命名空间的好处就是在定义该命名空间下的元素、属性和类型时,不需要增加前缀。
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean></bean> <aop:scoped-proxy/> </beans>
3.4.6. W3C的元素和属性
如果我们的XML文件中需要使用W3C提供的元素和属性,那么可以不在schemaLocation属性中指定XSD文件的位置,但一定要定义名称空间,例如:
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd > </beans>
上面定义了一个名称空间,前缀为xsi,名称空间为http://www.w3.org/2001/XMLSchema-instance。这个名称空间无需在schemaLocation中不存在这个名称空间。
你可能已经发现了,schemaLocation这个属性其实是w3c定义的属性,与元素一定,属性也需要指定“出处”,xsi:schemaLocation中的xsi就是名称空间前缀。也就是说,上面我们在没有指定xsi名称空间时,就直接使用schemaLocation是错误的。
4. JAXP解析
4.1. JAXP介绍
4.1.1. XML解析规范
XML文档本身是结构化文档,如果使用普通文件I/O进行读写,不仅效率很低,而且编程复杂。为了能更好地处理XML文档解析问题,W3C提供了DOM模型的推荐标准,DOM模型将XML结构化文档映射为一系列具有父子、兄弟关联关系的节点对象集。除此之外,XML行业还提供另一个XML解析规范 —— SAX,它采用一种基于事件处理的方式来解析XML文档。
- DOM:Document Object Model,即文档对象模型,它是有W3C推荐的处理XML文档的规范。
DOM为解析XML文档定义了一组标准接口,DOM解析器负责读入整个XML文档,然后将该文档转换成常驻内存的树状结构 —— 这棵树称为DOM树。
-
- 优点:元素与元素之间的结构关系保留下来。
- 缺点:需要一次性读取整个XML文档,导致系统开销过大,可能出现内存溢出的现象。
- SAX:Simple API for XML,它并不是W3C推荐的标准,是社区讨论的产物。
SAX要求在开始解析之前用户提供一个接口的实现对象,然后把接口实现对象传递给SAX解析器,然后在SAX解析器的过程中不调用实现对象的方法。
-
- 优点:适合解析大XML文件(内存空间占用小),因为是解析一行处理一行,处理完了就不需要在保留数据了。
- 缺点:因为是解析一行处理一行,解析之后数据就丢失了,所以元素与元素之间的结构关系没有保留下来。
4.1.2. 什么是JAXP
我们知道有很多像xerces一样的解析器,都对DOM和SAX提供了实现,那么如果我们在项目中一开始使用了解析A,然后因为某些原因想更换成解析B,那么就需要修改项目。
JAXP(Java API for XML Processing)是由Java提供的,JAXP是对所有像xerces一样的解析的提供统一接口的API。
当我们使用JAXP完成解析工作时,还需要为JAXP指定xerces或其他解析器,当需要更换解析器时,无需修改代码,只需要修改配置即可。
JAXP不是解析器,但使用它可以方便的切换解析器。所以在我们的程序中只会使用JAXP,而不会直接使用Xeces。
4.1.3. 百度百科
JAXP是Java API for XML Processing的英文字头缩写,中文含义是:用于XML文档处理的使用Java语言编写的编程接口。JAXP支持DOM、SAX、XSLT等标准。为了增强JAXP使用上的灵活性,开发者特别为JAXP设计了一个Pluggability Layer,在Pluggability Layer的支持之下,JAXP既可以和具体实现DOM API、SAX API 的各种XML解析器(XML Parser,例如Apache Xerces)联合工作,又可以和具体执行XSLT标准的XSLT处理器(XSLT Processor,例如Apache Xalan)联合工作。
4.1.4. 维基百科
JAXP(Java API for XML Processing,意为XML处理的Java API)是Java XML程序设计的应用程序接口之一,它提供解析和验证XML文档的能力。JAXP是在Java社区进程下开发的,包括JSR 5 (JAXP 1.0)和 JSR 63 (JAXP 1.1和1.2)两个规范。
JAXP解析XML的三种基本接口为:
- 文档对象模型解析接口或DOM接口。
- XML简单API解析接口或SAX接口。
- XML流API或StAX接口。
4.2. JAXP的DOM支持
4.2.1. DOM描述XML
使用DOM解析XML文档时,需要将XML文档转换成一颗DOM树。而DOM解析器在载入XML文档时,会将XML文档的节点转换成对应DOM树的节点。
例如以下这个XML文档:
<?xml version="1.0" encoding="UTF-8"?> <person> <name>baidu</name> <age>15</age> </person>
使用DOM树解析后,就转换成以下的DOM树了。
4.2.2. DOM解析器
在程序中获取DOM解析器按如下两个步骤进行:
- 调用DocumentBuilderFactory类的newInstance()方法获取解析器工厂对象。
- 调用DOM解析器工厂的newDocumentBuilder()方法获取DOM解析器。
JAXP使用DocumentBuilder抽象类代表DOM解析器,抽象类里包含了多个重载的parse()方法:
- Document parse(File f):将File对象对应的XML文档解析成Document对象。
- Document parse(InputSource is):将InputSource输入源里包含的XML文档解析成Document对象。
- Document parse(InputStream is):将InputStream输入源里包含的XML文档解析成Document对象。
- Document parse(InputStream is, String systemId):将InputStream输入源里包含的XML文档解析成Document对象,systemId参数用于指定URI。
- Document parse(String uri):将URI输入源里包含的XML文档解析成Document对象。
这些parse()方法用于将不同形式的XML文档解析成Document对象,一旦获得了XML文档的Document对象,XML文档就完全转换为对象模型了,整个XML文档的内容都将转换成DOM树里的各节点对象,然后就可以利用这些对象的属性和方法来获取XML文档里的数据了。
4.2.3. DOM解析XML
一旦获得了XML文档对应的Document对象,就可以利用DOM节点的各种方法来访问XML文档里的数据了。
Document对象中包含如下用来访问 XML文档的节点方法:
- Element getDocumentElement():用于获取XML文档的根节点。
- Element getElementById(String ElementId):用于根据XML元素的ID来获取XML元素。
- NodeList getElementByTagName(String TagName):用于根据XML元素的标签名来获取XML元素。
DOM树中的所有节点都是Node对象,每个Node对象都可能包括nodeName、nodeValue和attributes等属性,不同的Node节点对这3个属性的支持是不同的。
下面的例子示范了DOM是如何解析XML文档的:
@org.junit.Test public void Test() throws Exception{ //创建解析器工厂对象 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //获取DOM解析器 DocumentBuilder builder = factory.newDocumentBuilder(); //读取XML文档 Document document = builder.parse(new File("04_dom/person.xml")); //获取XML文档的根元素即文档元素 Element root = document.getDocumentElement(); //获取根元素下所有的子元素 NodeList children = root.getChildNodes(); //遍历所有子元素 for(int i = 0; i < children.getLength(); i++){ //获取其中每一个节点 Node node = children.item(i); //判断节点的类型是否为元素 if(node.getNodeType() == Node.ELEMENT_NODE){ //强行转换成元素类型 Element element = (Element) node; String name = element.getElementsByTagName("name").item(0).getTextContent(); String qq = element.getElementsByTagName("qq").item(0).getTextContent(); String weixin = element.getElementsByTagName("weixin").item(0).getTextContent(); String blog = element.getElementsByTagName("blog").item(0).getTextContent(); System.out.println(name+": qq = "+qq+", weixin = "+weixin+", blog = "+blog); } } }
4.3. JAXP的SAX支持
4.3.1. SAX的处理机制
SAX与DOM的解析完全不同,它采用事件机制的方式来解析XML文档,这是一种快速读写XML数据的方式。使用SAX解析器对XML文档进行解析时,会触发一系列事件,这些事件将被响应的事件监听器监听,从而触发响应的事件处理方法,程序通过这些事件处理方法实现对XML文档的访问。
以下显示了SAX解析XML文档的处理流程。
4.3.2. SAX解析器
JAXP为SAX解析器提供了如下2组API:
- XMLReader和XMLReaderFactory:XMLReaderFactory工厂类的createXMLReader()静态方法用于创建XMLReader。XMLReader和XMLReaderFactory位于org.xml.sax包及其子包下。
- SAXParser和SAXParserFactory:SAXParserFactory工厂类的newSAXParser()实例方法用于创建SAXParser。SAXParser和SAXParserFactory位于javax.xml.parsers包下。
上面两种API中的XMLReader和SAXParser都是SAX解析器,它们都定义了多个parse()方法,用于以SAX方式解析XML文档。
XMLReader定义了如下两个用于解析XML文档的方法:
- void parse(InputSource input):解析InputSource输入源中的XML文档。
- void parse(String systemId):解析系统URI(磁盘文件是URI的一种)所代表的XML文档。
SAXParser则定义了更多的方法用于解析XML文档:
- void parse(File f, DefaultHandler dh):使用指定的dh作为监听器监听SAX解析事件,解析f文件所代表的XML文档。
- void parse(InputSource is, DefaultHandler dh):使用指定的dh作为监听器监听SAX解析事件,解析is输入源中的XML文档。
- void parse(InputStream is, DefaultHandler dh):使用指定的dh作为监听器监听SAX解析事件,解析is输入流中的XML文档。
- void parse(String uri, DefaultHandler dh):使用指定的dh作为监听器监听SAX解析事件,解析系统URI所代表的XML文档。
对比XMLReader和SAXParser,不难发现SAXParser包含了更多的parse方法,因此用起来更加方法。SAXParser除了可解析URI和InputSource所代表的XML文档之外,还可解析File对象和InputStream所代表的XML文档,显然使用更加方便。
4.3.3. SAX解析XML
使用SAXParser和SAXParserFactory这组API解析XML文档:
1) 通过SAXParserFactory的newInstance()方法创建SAX解析器工厂对象。
2) 调用SAXParserFactory对象的newSAXParser()方法创建SAXParser对象(SAX解析器)。
3) 调用SAXParser对象的parse()方法解析XML文档,调用该方法时需要传入一个DefaultHandler对象。
public class Test2 { @org.junit.Test public void Test() throws Exception{ //创建SAX解析器工厂对象 SAXParserFactory factory = SAXParserFactory.newInstance(); //创建SAX解析器对象 SAXParser parser = factory.newSAXParser(); //开始解析文档 parser.parse("04_jaxp/person.xml", new SaxHandler()); } } class SaxHandler extends DefaultHandler{ //定义一个成员变量来保存XML文档的元素 private String element; //每当处理XML文档数据时,将触发该方法 @Override public void characters(char[] ch, int start, int length) throws SAXException { String content = new String(ch, start, length); if(content.trim().length() > 0){ System.out.println("<"+element+">元素的值是:"+content.trim()); } } //处理XML文档数据结束时,触发该方法 @Override public void endDocument() throws SAXException { System.out.println("解析文档结束."); } //处理元素结束时,触发该方法 @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("处理元素结束:"+qName); } //处理XML文档数据开始时,触发该方法 @Override public void startDocument() throws SAXException { System.out.println("解析文档开始."); } //处理元素开始时,触发该方法 @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("处理元素开始:"+qName); element = qName; } }
使用XMLReader和XMLReaderFactory这组API解析XML文档:
1) 调用XMLReaderFactory的createXMLReader()方法创建XMLReader对象(SAX解析器)。
2) 如果希望启用SAX解析器的原生特性,可多次调用XMLReader对象setFeature()方法。
3) 分别调用XMLReader对象的setContentHandler、setDTDHandler、setEntityResolver和setErrorhandler方法绑定多个事件监听函数。
4) 调用XMLReader的parse()方法解析XML文档。
public class Test { @org.junit.Test public void Test() throws Exception{ //创建XMLReader解析器 XMLReader reader = XMLReaderFactory.createXMLReader(); //注册ContentHandler监听器 reader.setContentHandler(new SaxHandler()); //开始解析文档 reader.parse("04_jaxp/person.xml"); } } class SaxHandler extends DefaultHandler{ //定义一个成员变量来保存XML文档的元素 private String element; //每当处理XML文档数据时,将触发该方法 @Override public void characters(char[] ch, int start, int length) throws SAXException { String content = new String(ch, start, length); if(content.trim().length() > 0){ System.out.println("<"+element+">元素的值是:"+content.trim()); } } //处理XML文档数据结束时,触发该方法 @Override public void endDocument() throws SAXException { System.out.println("解析文档结束."); } //处理元素结束时,触发该方法 @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("处理元素结束:"+qName); } //处理XML文档数据开始时,触发该方法 @Override public void startDocument() throws SAXException { System.out.println("解析文档开始."); } //处理元素开始时,触发该方法 @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("处理元素开始:"+qName); element = qName; } }
5. dom4j处理XML
5.1. dom4j介绍
5.1.1. 什么是dom4j
DOM4J是dom4j.org出品的一个开源XML解析包,Dom4j是一个易用的、开源的库,用于XML,XPath和XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
5.1.2. 百度百科
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常非常优秀的JavaXML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,dom4j无论在哪个方面都是非常出色的。如今你可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这是必须使用的jar包, Hibernate用它来读写配置文件。
5.1.3. 下载dom4j
访问dom4j官方网站:http://www.dom4j.org,下载其最新版本。
下载完成后得到dom4j-1.6.1.zip文件,将其解压缩可得到如下目录结构:
- docs:该目录下存放了dom4j的说明文件和API文档。
- lib:该目录下存放了dom4j编译和运行所依赖的第三方类库。
- src:该目录下存放了dom4j项目的所有源文件。
- xdocs:该目录下存放了dom4j的一些相关文档,只是这些文档都是xml格式的。
- xml:该目录下存放了dom4j提供的一些简单的范例。
- dom4j-1.6.1.jar的核心JAR包。
5.2. 解析XML
5.2.1. 提供接口及解析机制
dom4j常用接口说明
dom4j对底层原始的XML解析器进行了高度封装,正是这种封装简化了XML处理。在dom4j的org.dom4j.io包下提供了如下几个类:
- DOMReader:它负责根据W3C的DOM树创建dom4j树。
- SAXReader:它基于SAX解析机制来解析XML文档,并将其转换为dom4j树。
- XPP3Reader:其底层需要依赖于XML Pull Parser 3.x来解析XML文档,并将其转换为dom4j树。
- XPPReader:它是基于XML Pull Parser 2.x的解析器,目前不支持注释、DCATA和处理指令。
5.2.2. 读取XML
根据dom4j底层提供的不同的XML解析器,可以有以下几种方式读取XML文档:
1) SAXReader解析器方式:
public class Test { @org.junit.Test public void Test() throws Exception { SAXReader reader = new SAXReader(); Document document = reader.read(new File("04_jaxp/person.xml")); } }
2) XPP3Reader解析器方式:
public class Test { @org.junit.Test public void Test() throws Exception { XPP3Reader reader = new XPP3Reader(); Document document = reader.read(new File("04_jaxp/person.xml")); } }
3) XPPReader解析器方式:
public class Test1 { @org.junit.Test public void Test() throws Exception { XPPReader reader = new XPPReader(); Document document = reader.read(new File("04_jaxp/person.xml")); } }
5.2.3. 解析XML
通过解析器读取XML文档完成后,使用dom4j解析XML文档:
1) 通过Document对象的getRootElement()方法获取根元素;
2) 通过Element对象的elements()方法获取根元素的所有子元素;
3) 遍历所有子元素,得到解析后的内容。
public class Test { private static void iterDocument(Document document) { //获取XML文档的根元素 Element rootElement = document.getRootElement(); //获取根元素下的所有子元素 List<Element> elements = rootElement.elements(); //遍历所有子元素集合 for (Element element : elements) { //获取元素名称 String eleName = element.getName(); //获取name子元素的文本内容 String name = element.elementText("name"); //获取age子元素的文本内容 String age = element.elementText("age"); System.out.println(eleName + ": [name = " + name + ", age = " + age + "]"); } } }
5.2.4. 修改XML
通过dom4j修改XML文档需要如下几步:
1) 读取XML文档,得到Document对象及根元素;
2) 对解析得到的XML文档做修改、增加及删除操作;
3) 将修改后的内容,回写到当前XML文档中。
public class Test { private void updateDocument(Document document) throws Exception { //获取XML文档的根元素 Element rootElement = document.getRootElement(); //获取根元素下的所有子元素 List<Element> elements = rootElement.elements(); /***************修改元素****************/ //获取根元素下第一个person元素 Element firElement = elements.get(0); //获取person元素下blog元素 Element blog = firElement.element("blog"); //修改blog元素的文本内容 blog.setText("http://www.baidu.com"); /***************增加元素****************/ //为XML文档的根元素,增加新的子元素person Element newElement = rootElement.addElement("person"); //为新增加的子元素,设置属性 newElement.addAttribute("number", "003"); //为新增加的子元素,添加一个name子元素,文本内容为baidu newElement.addElement("name").setText("baidu"); /***************删除元素****************/ //获取根元素下第二个person元素 Element secElement = elements.get(1); //获取第二个person元素下,需要删除的元素 Element delElement = secElement.element("blog"); //删除第二个person元素下的blog元素 secElement.remove(delElement); /***************回写XML****************/ //创建格式化器,使用 缩进,添加换行 OutputFormat format = new OutputFormat(" ", true); //清空数据中原有的换行 format.setTrimText(true); //创建XML输出流对象 XMLWriter writer = new XMLWriter(new FileOutputStream("04_jaxp/person.xml")); //输出Document writer.write(document); //关闭输出流 writer.close(); } }
5.2.5. 创建XML
dom4j还提供了创建XML文档的方法:
Document document = DocumentHelper.createDocument();
5.2.6. 常用API
Node方法:
- String asXML():把当前节点转换成字符串,如果当前Node是Document,那么就会把整个XML文档返回;
- String getName():获取当前节点名字;Document的名字就是绑定的XML文档的路径;Element的名字就是元素名称;Attribute的名字就是属性名;
- Document getDocument():返回当前节点所在的Document对象;
- short getNodeType():获取当前节点的类型;
- String getNodeTypeName():获取当前节点的类型名称,例如当前节点是Document的话,那么该方法返回Document;
- String getStringValue():获取当前节点的子孙节点中所有文本内容连接成的字符串;
- String getText():获取当前节点的文本内容。如果当前节点是Text等文本节点,那么本方法返回文本内容;例如当前节点是Element,那么当前节点的内容不是子元素,而是纯文本内容,那么返回文本内容,否则返回空字符串;
- void setDocument(Document doc):给当前节点设置文档元素;
- void setParent(Element parent):给当前节点设置父元素;
- void setText(String text):给当前节点设置文本内容;
Branch方法:
- void add(Element e):添加子元素;
- void add(Node node):添加子节点;
- void add(Comment comment):添加注释;
- Element addElement(String eleName):通过名字添加子元素,返回值为子元素对象;
- void clearContent():清空所有子内容;
- List content():获取所有子内容,与获取所有子元素的区别是,<name>liSi</name>元素没有子元素,但有子内容;
- Element elementById(String id):如果元素有名为“ID”的属性,那么可以使用这个方法来查找;
- int indexOf(Node node):查找子节点在子节点列表中的下标位置;
- Node node(int index):通过下标获取子节点;
- int nodeCount():获取子节点的个数;
- Iterator nodeIterator():获取子节点列表的迭代器对象;
- boolean remove(Node node):移除指定子节点;
- boolean remove(Commont commont):移除指定注释;
- boolean remove(Element e):移除指定子元素;
- void setContent(List content) :设置子节点内容;
Document方法:
- Element getRootElement():获取根元素;
- void setRootElement():设置根元素;
- String getXmlEncoding():获取XML文档的编码;
- void setXmlEncoding():设置XML文档的编码;
Element方法:
- void add(Attribute attr):添加属性节点;
- void add(CDATA cdata):添加CDATA段节点;
- void add(Text Text):添加Text节点;
- Element addAttribute(String name, String value):添加属性,返回值为当前元素本身;
- Element addCDATA(String cdata):添加CDATA段节点;
- Element addComment(String comment):添加属性节点;
- Element addText(String text):添加Text节点;
- void appendAttributes(Element e):把参数元素e的所有属性添加到当前元素中;
- Attribute attribute(int index):获取指定下标位置上的属性对象;
- Attribute attribute(String name):通过指定属性名称获取属性对象;
- int attributeCount():获取属性个数;
- Iterator attributeIterator():获取当前元素属性集合的迭代器;
- List attributes():获取当前元素的属性集合;
- String attributeValue(String name):获取当前元素指定名称的属性值;
- Element createCopy():clone当前元素对象,但不会copy父元素。也就是说新元素没有父元素,但有子元素;
- Element element(String name):获取当前元素第一个名称为name的子元素;
- Iterator elementIterator():获取当前元素的子元素集合的迭代器;
- Iterator elementIterator(String name):获取当前元素中指定名称的子元素集合的迭代器;
- List elements():获取当前元素子元素集合;
- List elements(String name):获取当前元素指定名称的子元素集合;
- String elementText(String name):获取当前元素指定名称的第一个元素文件内容;
- String elementTextTrime(String name):同上,只是去除了无用空白;
- boolean isTextOnly():当前元素是否为纯文本内容元素;
- boolean remove(Attribute attr):移除属性;
- boolean remove(CDATA cdata):移除CDATA;
- boolean remove(Text text):移除Text。
DocumentHelper静态方法:
- static Document createDocument():创建Dcoument对象;
- static Element createElement(String name):创建指定名称的元素对象;
- static Attribute createAttrbute(Element owner, String name, String value):创建属性对象;
- static Text createText(String text):创建属性对象;
- static Document parseText(String text):通过给定的字符串生成Document对象;
5.3. XPath语言
5.3.1. 什么是XPath
XPath语言是一门专门用于在XML文档中查找信息的语言,其他XML程序可利用XPath在XML文档中对元素和属性进行导航。XPath主要用于为XSTL、XPointer以及其他XML技术提供服务,XSTL、XPointer等技术需要依赖XPath来定位XML文档中的元素和属性等节点。
1999年11月,W3C发布了XSTL 1.0标准,而XPath 1.0作为XSTL 1.0的重要组成部分也被一同发布。2007年1月,W3C发布了最新的XSTL 2.0规范,与其一同发布的还有XPath 2.0,这也是XPath语言的最新版本。
5.3.2. 百度百科
XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言。XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初 XPath 的提出的初衷是将其作为一个通用的、介于XPointer与XSL间的语法模型。但是 XPath 很快的被开发者采用来当作小型查询语言。
5.3.3. 维基百科
XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。
XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初XPath的提出的初衷是将其作为一个通用的、介于XPointer与XSL间的语法模型。但是XPath很快的被开发者采用来当作小型查询语言。
5.3.4. 解析XML
public class Test2 { @org.junit.Test public void Test() throws Exception { //创建XML解析器 SAXReader reader = new SAXReader(); Document document = reader.read(new File("04_jaxp/person.xml")); //定义XPath表达式 String expr = "//name"; //利用XPath表达式,查询XML文档元素 List<Element> elements = document.selectNodes(expr); //遍历输出所有文本内容 for (Element element : elements) { System.out.println(element.getText()); } } }
- XPath表达式:
/students/student |
通过绝对路径获取students根节点下所有的直接子节点student元素对象 |
students/student |
通过相对路径获取students根节点下所有的直接子节点student元素对象 |
//name |
获取所有name元素对象,不考虑位置 |
student//name |
获取student元素下所有的name元素对象 |
//@id |
获取所有的id属性对象 |
//student[@id] |
获取所有带id属性的student元素对象 |
//student[@id=‘002'] |
获取id等于002的student元素对象 |
//student[age>20] |
获取所有子元素age的值大于20的student元素对象 |
- 利用Document对象的方法,加载XPath表达式:
document.selectNodes(expr);
document.selectSingleNode(expr);
- 参看W3C规范手册。
6. JDOM处理XML
6.1. JDOM介绍
6.1.1. 什么是JDOM
与dom4j操作非常相似的,还有JDOM。就功能而言,dom4j和JDOM非常类似,都是为了弥补Xerces-J的繁琐难用等缺陷而产生。实际上,dom4j和JDOM之间渊源颇深,由于W3C的DOM规范要考虑处理XML文档,又要考虑处理HTML文档,因此实际开发过程中使用DOM解析器来处理XML文档显得异常繁琐。有一帮人开始开发Java专用的XML API,希望可以开发一套更便于使用的XML API,这就是JDOM的由来。
但在JDOM开发一半的时候,有一部分人产生了一些新的想法,而这些想法又无法在JDOM中得到实现,于是他们干脆从JDOM项目组中分离出来,单独去开发另一套Java专属的XML API,结果就得到了之前我们学习的dom4j。
6.1.2. 百度百科
JDOM是一种使用 XML(标准通用标记语言下的一个子集) 的独特 Java 工具包,用于快速开发 XML 应用程序。它的设计包含 Java 语言的语法乃至语义。
JDOM是两位著名的 Java 开发人员兼作者,Brett Mclaughlin 和 Jason Hunter 的创作成果, 2000 年初在类似于Apache协议的许可下,JDOM作为一个开放源代码项目正式开始研发了。它已成长为包含来自广泛的 Java 开发人员的投稿、集中反馈及错误修复的系统,并致力于建立一个完整的基于 Java 平台的解决方案,通过 Java 代码来访问、操作并输出 XML 数据。
6.1.3. 下载JDOM
访问JDOM官方网站:http://www.jdom.org,下载其最新版本。
下载完成后得到jdom-2.0.5.zip文件,将其解压缩可得到如下目录结构:
- lib:该目录下存放了JDOM编译和运行所依赖的第三方类库。
- jdom-2.0.5.jar:该JAR包是JDOM的核心包。
- jdom-2.0.5-javadoc.jar:该JAR包是JDOM的帮助文档。
- jdom-2.0.5-sources.jar:该JAR包是JDOM的源代码。
6.2. 解析XML
6.2.1. 提供类及解析机制
JDOM常用类说明
除此之外,JDOM在org.jdom.input包下提供了如下两个类用于构建JDOM树:
- DOMBuilder:它负责将一份已有的W3C的Document对象转换为JDOM的Document。
- SAXBuilder:它是一个广泛使用的工具类,可将来自输入流、磁盘或指定URL所代表的XML文档转换为JDOM的Document。
6.2.2. 读取XML
JDOM读取XML文档方式,是利用SAXBuilder解析器来加载:
public class Test { @org.junit.Test public void Test() throws Exception { //使用SAXBuilder来解析XML文档 SAXBuilder builder = new SAXBuilder(); //解析XML文档 Document document = builder.build(new File("04_jaxp/person.xml")); //获取XML文档的根元素 Element rootElement = document.getRootElement(); } }
6.2.3. 解析XML
通过解析器读取XML文档完成后,使用JDOM解析XML文档:
1) 通过Document对象的getRootElement()方法获取根元素;
2) 通过Element对象的getChildren ()方法获取根元素的所有子元素;
3) 遍历所有子元素,得到解析后的内容。
public class Test { private void iterDocument(Element rootElement) { //获取根元素下的所有子元素 List<Element> elements = rootElement.getChildren(); //遍历所有子元素集合 for (Element element : elements) { if (element.getChildren().size() > 0) { iterDocument(element); } else { System.out.println(element.getName()+" --> "+element.getText()); } } } }
6.2.4. 修改XML
使用JDOM可以非常方便地修改XML文档,步骤如下:
1) 解析XML文档,将XML文档转换为JDOM树。
2) 利用JDOM树提供的导航方法找到需要修改的节点。
3) 修改指定节点内容。
4) 输出修改过的JDOM树。
public class Test { private void updateDocument(Element rootElement, Document document) throws Exception { //创建一个新的person元素 Element person = new Element("person"); //为新创建的person元素设置number属性 person.setAttribute("number", "005"); //为person元素,创建一个name子元素 Element name = new Element("name"); //为新创建的name元素设置文本内容 name.setText("longestory"); //将name子元素添加到person元素下 person.addContent(name); //将person元素添加到根元素下 rootElement.addContent(person); //创建XML输出器 XMLOutputter out = new XMLOutputter(); //创建XML输出流 FileOutputStream fos = new FileOutputStream("04_jaxp/person.xml"); //执行输出 out.output(document, fos); //关闭输出流 fos.close(); } }