02.XML技术

一、XML简介

XML指可扩展标记语言(eXtensible Markup Language),是用来传输和存储数据的,是一种通用的数据交换格式,可以使数据在不同应用程序间交换,具有很强的可扩展性。

 

  • 可扩展,是指用户可以按照XML规则自定义标记,英文、中文皆可。
  • 标记语言,很类似于HTML。XML不是代替HTML的,是对HTML的补充。

W3C在1998年2月发布1.0版本;W3C在2004年2月发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本!!!

XML是不作为的,就是说“XML不会做任何事情”。XML被设计用来结构化、存储和传输信息。

XML的作用:

  • 数据存储:可作为小型关系数据库,表示数据之间的关系,用来当数据库存储数据。
  • 数据交换:不同语言之间、程序之间、系统之间交换数据。
  • 程序的配置文件:这也是大家使用XML最常见的目的。比如,连接数据库时,在xml配置文件中定义数据库连接地址、用户名和密码,如果修改数据库的信息,不需要修改程序源代码,只要修改配置文件就可以了。

XML与HTML的比较:

  • HTML是用来显示数据,其焦点是数据的外观;XML是用来传输和存储数据,其焦点是数据的内容。
  • HTML是不区分大小写的,而XML是严格区分大小写的。
  • HTML可以有多个根元素,而格式良好的XML有且只能有一个根元素
  • HTML中属性值的引号是可用可不用的,XML中的属性值必须放在引号中。
  • HTML中空格是自动过滤的,而XML中空格则不会自动删除。
  • HTML中的标记是预定义的,而XML中的标记可以根据需要自己定义,并且可扩展。

我们举个例子来看一下我们身边的XML:

中国有很多省份,每个省份下又有很多城市,这些省市之间的关联关系可以通过一张树状结构图来描述,如图所示。

 

图中直观地描述了中国与所辖省、市之间的层次关系。但是对于程序而言,解析图片内容非常困难,这时,采用XML文件保存这种具有树状结构的数据是最好的选择。

下面的代码就可以描述出上图所示的关系。

<?xml version="1.0" encoding="UTF-8"?>
<中国>
    <河北>
        <城市>张家口</城市>
        <城市>石家庄</城市>
    </河北>
    <山西>
        <城市>太原</城市>
        <城市>大同</城市>
    </山西>
</中国>

二、XML语法

一个基本的XML文档通常是由文档声明(序言)文档元素两部分组成。下面我们来分别介绍一下。

1、文档声明

在一个XML文档中,必须包含一个XML文档声明,并且必须位于文档的第一行。语法格式:

<?xml 版本信息 [编码信息] [文档独立性信息]?>

文档声明以符号“<?”开头,以符号“?>”结束,中间可以声明版本信息、编码信息以及文档独立性信息。

需要注意的是,在“<”和“?”之间、“?”和“>”之间以及第一个“?”和xml之间不能有空格

另外,中括号([])括起来的部分是可选的。完整的代码如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  • version属性表示XML的版本。目前,最常用的XML版本是1.0。
  • encoding属性用来说明XML文档所使用的编码方式,默认情况下,XML文档使用的是UTF-8编码方式。
  • standalone属性用来声明这个文档是否是独立的文档,默认情况下,standalone属性的值为no,表示文档依赖于外部文档。

这里提到的依赖,是指文档需要DTD文件验证其中的标识是否有效,或者需要CSS(层叠样式表,HTML样式表)、XSL(可扩展样式表语言,XML样式表)文件来控制显示外观。

编码详解:

我们来编写一段代码:

<?xml version="1.0" encoding="UTF-8"?>
<note>
    <from>Tom</from>
    <to>Jerry</to>
    <message>你好</message>
</note>

在XML文档中不指定任何编码属性时,我们使用Windows记事本保存XML要注意:

如果XML 中包含非ASCII字符(中文、法文等),我们将文件“另存为”编码ANSI后,使用IE浏览器浏览将会出错。

ANSI编码是一种对ASCII码的拓展,ASCII能表示128个字符,ANSI能表示256个字符。

 

如果将文件“另存为”编码UTF-8或Unicode后,使用IE浏览器浏览将不会出错。

 

如果将文件“另存为”编码UTF-8或Unicode后,而XML的encoding指定为"GB2312",使用IE浏览器浏览汉字将会显示乱码。

 

所以XML文档的保存编码一定要和文档声明中的encoding一致起来,最好都使用UTF-8。

 

【补充知识】ASCII、ANSI、GB2312、GBK、Unicode、UTF-8编码:

① ASCII:首先明白计算机能识别的都是二进制010101010这种代码,但是这些010101010人是难以看懂的,于是美国人就用ASCII码(American Standard Code for Information Interchange,美国信息交换标准码)制作了一张表,里面包含从a、b、c.......@%$等128个字符。但是,美国用的字母,德国、法国,还有咱们中国汉字几万个,一张表存不下。

 

② ANSI:扩展了ASCII,能表示256个字符。

③ GB2312、GBK:汉字内码扩展规范(GBK即“国标”、“扩展”汉语拼音的第一个字母)。GBK是在GB2312标准基础上的内码扩展规范。GBK编码规定,计算机不能在每次都只读一个字节(00000000,8位表示一个字节),而是要先看看第一位是不是为0,要是为0的话,就当作ASCII码来读入一个字节,不然的话就读入两个字节(汉字太多一个字节存不下,读入两个字节表示汉字,就查GBK)。

④ Unicode:那么每个国家一个编码表,相互通信的时候由于解码方式不同就会导致乱码(用ASCII发邮件,计算机查ASCII表转换成对应0101010二进制发送出去,接收的人用GBK解码,将0101010查GBK表肯定就查不到啊)。于是,国际组织就发明了一套公用的表Unicode编码,将所有国家,所有字符都收进去了,从0一直到100多万(用三个字节表示)。

由于历史方面的原因,我们经常说的Unicode 通常就是 UTF-16。

⑤ UTF-8:虽然有了世界通用的表Unicode,但是有人就发现这有点浪费资源啊。每次让计算机读取三个字节,然后参照Unicode表解码,那么像a、b...0、1、2...这些一个字节就够了的就太浪费了。于是UTF-8、UTF-16、UTF-32这些编码方案就出现了。UTF-16是用两个字节来编码所有的字符,UTF-32则选择用4个字节来编码,UTF-8为了节省资源,采用变长编码,编码长度从1个字节到4个字节不等。由于互联网大部分是1个字节(代码很多英文的),所以最后大家选择用的最多的还是UTF-8。

2、元素定义

在XML文档中,主体内容都是由元素(Element)组成的。元素一般是由开始标记、属性、元素内容和结束标记构成,具体示例如下:

<城市 类型="直辖市">北京</城市>

上面的示例中,“<城市>”和“</城市>”就是XML文档中的标记,标记的名称也就是元素的名称。在一个元素中可以嵌套若干子元素。

如果一个元素没有嵌套在其它元素内,则这个元素称为根元素。根元素是XML文档定义的第一个元素。

如果一个元素中没有嵌套子元素,也没有包含文本内容,则这样的元素称为空元素,空元素可以不使用结束标记,但必须在开始标记的结束符“>”前增加一个正斜杠“/”来说明该元素是个空元素,例如:<img></img>可以简写成<img/>。

在XML文档中,元素的名称可以包含字母、数字以及其他一些可见的字符,但是在命名XML元素时,应该遵守以下规范。

(1)XML是强标记语言,区分大小写,例如: <P>和<p>是两个不同的标记。

(2)元素名称中,不能包含空格、冒号、分号、逗号和尖括号等,元素不能以数字开头,否则XML文档会报错。<7eleven />

(3)建议不要使用“.”,因为在很多程序语言中,“.”用于引用对象的属性。<user.id />

(4)建议不要用减号(-),而用下划线(_)代替,以避免与表达式中的减号(-)运算符发生冲突。<user-name />

(5)建议元素名称不要以字符组合xml(或XML、或Xml等)开头。<xmlfile />

(6)建议元素名称的大小写尽量采用同一标准,要么全部大写,要么全部小写。

(7)元素名称可以使用非英文字符,例如中文,但有些软件可能不支持英文字符以外的字符,在使用时应考虑这种情况。

3、属性定义

在XML文档中,可以为元素定义属性。属性是对元素的进一步描述和说明。在一个元素中,可以有多个属性,并且每个属性都有自己的名称和取值,具体示例如下:

<售价 单位="元">68</售价>

在上面的示例中,<售价>元素中定义了一个属性“单位”。需要注意的是,在XML文档中,属性的命名规范同元素相同,属性值必须要用双引号(")或者单引号(')引起来,否则被视为错误。

4、注释

如果想在XML文档中插入一些附加信息,比如作者姓名、地址或电话等信息,或者想暂时屏蔽某些XML语句,这时,可以通过注释的方式来实现,被注释的内容会被程序忽略而不被解析和处理。XML注释和HTML注释写法基本一致,具体语法格式如下所示:

<!-- 注释信息 -->
  • 注释不能嵌套:  <!--  <!-- -->  -->
  • 注释也不能放到第一行,第一行第一列必须放文档声明。

5、特殊字符处理(转义字符)

和HTML类似

预定义实体

特殊字符

含义

&lt;

小于

&gt;

大于

&amp;

&

和号

&apos;

'

单引号

&quot;

"

引号

6、CDATA区(格式记住)

Character Data,字符数据,指一段不想被程序解析的原始数据,这里面的数据会原封不动的显示出来,不会被转义。

<![CDATA[
    一段代码
]]>

例如,想直接存储以下代码,而不使用转义字符。if(a<b && b<c && d>f) {}

代码: <![CDATA[ if(a<b && b<c && d>f) {} ]]> 

把特殊字符,当做文本内容,而不是标签。

注意:

① CDATA 部分不能包含字符串 "]]>",也不允许嵌套的 CDATA 部分。

② 标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。

三、约束

在现实生活中,如果一篇文章的语法正确,但内容包含违法言论或逻辑错误,这样的文章是不允许发表的。

同样,在编写XML文档时,其内容必须满足某些条件的限制,例如以下代码:

<?xml version="1.0" encoding="UTF-8"?>
<书架>
    <>
        <书名>JavaWeb程序开发入门</书名>
        <作者>传智播客</作者>
        <售价>60</售价>
        <售价>30</售价>
    </>
</书架>

在上面的示例中,尽管这个XML文档结构是正确的,用IE浏览器打开它也不会出现任何问题,但是,由于XML文档中的标记是可以随意定义的,同一本书出现了两种售价,如果仅根据元素名称区分哪个是原价,哪个是会员价,这是很难实现的。为此,在XML文档中,定义了一套规则来对文档中的内容进行约束,这套约束称为XML约束。

对XML文档进行约束时,同样需要遵守一定的语法规则,这种语法规则就形成了XML约束语言。目前,最常用的两种约束语言是DTD约束Schema约束

四、DTD约束

DTD(Document Type Definition,文档类型定义)的作用是定义XML文档的合法构建模块,使用一系列的合法元素来定义文档结构。在一个DTD文件中,可以包含元素的定义、元素之间关系的定义、元素属性的定义以及实体和记号的定义。

1、DTD定义

我们来看实例代码book.xml

<?xml version="1.0" encoding="UTF-8"?>
<书架>
    <>
        <书名>JavaWeb程序开发入门</书名>
        <作者>传智播客</作者>
        <售价>60</售价>
    </>
    <>
        <书名>JavaWeb从入门到精通</书名>
        <作者>明日科技</作者>
        <售价>80</售价>
    </>
</书架>

与之对应的DTD约束文档book.dtd,代码如下:

<!ELEMENT 书架 (书+)>
<!ELEMENT 书 (书名,作者,售价)>
<!ELEMENT 书名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>

对book.dtd文档所规定的约束详细解释如下:

(1)在第1行中,使用<!ELEMENT…>语句定义了一个元素,其中“书架”是元素的名称,“(书+)”表示书架元素中有一个或者多个书元素,字符+用来表示它所修饰的成分必须出现一次或者多次。

(2)在第2行中,“书”是元素名称,“(书名,作者,售价)”表示元素书包含书名、作者、售价这三个子元素,并且这些子元素要按照顺序依次出现

(3)在第3-5行中,“书名”、“作者”和“售价”都是元素名称,“(#PCDATA)"表示元素的内容是可解析字符数据(Parsed Character Data),就是普通文本字符串。

那么,创建DTD文件步骤:

(1)看XML中有几个元素,在DTD中就写几个<!ELEMENT>。

(2)判断元素是简单元素还是复杂元素:

  • 简单元素:没有子元素,语法如下: <!ELEMENT 元素名称 (#PCDATA)> 
  • 复杂元素:有子元素的元素,语法如下: <!ELEMENT 元素名称 (子元素+)>  或  <!ELEMENT 元素名称 (子元素,子元素,……)> 

2、DTD的引入

有了DTD文件,如果想使用DTD文件来约束XML文档,必须在XML文档中引入DTD文件。

(1)外部引入

在XML文档中引入外部DTD文件有两种方式,具体如下:

<!DOCTYPE 根元素名称 SYSTEM "外部DTD文件URI">

<!DOCTYPE 根元素名称 PUBLIC "DTD名称" "外部DTD文件URI">

在上述两种引入DTD文件的方式中:

第一种方式用来引用本地的DTD文件,“外部DTD文件的URI”是指DTD文件的存放位置,它可以是相对于XML文档的相对路径,也可以是一个绝对路径;

第二种方式用来引用网络上公共的DTD文件,“外部DTD文件的URI”是指,Internet上的一个绝对URL地址。

如果希望引入一个公共的DTD文件,则需要在DOCTYPE声明语句中使用PUBLIC属性,具体示例如下:

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

其中"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"是DTD名称,它用于说明DTD符合的标准、所有者的名称以及对DTD描述的文件进行说明,虽然DTD名称看上去比较复杂,但这完全是由DTD文件发布者去考虑的事情,XML文件的编写者只要把DTD文件发布者事先定义好的DTD标识名称进行复制就可以了。

我们看看之前学习HTML的时候,HTML文档的第一行:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

这里也是一个公共DTD的引入。

引入DTD的book.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 书架 SYSTEM "book.dtd">
<书架>
    ……
</书架>

(2)内部嵌入

在XML中直接嵌入DTD定义语句的完整语法格式如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE 根元素名 [
    DTD定义语句
    ……
]>

上面的book.xml代码可以改写为:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE 书架 [
    <!ELEMENT 书架 (书+)>
    <!ELEMENT 书 (书名,作者,售价)>
    <!ELEMENT 书名 (#PCDATA)>
    <!ELEMENT 作者 (#PCDATA)>
    <!ELEMENT 售价 (#PCDATA)>
]>
<书架>
    ……
</书架>

不管是哪种方式,通过IE浏览器浏览,我们发现即使XML不符合DTD约束,浏览时也一样可以浏览出来。因为我们使用浏览器打开XML文件,浏览器只负责校验XML的语法,不负责校验约束。

如果想要校验约束,我们需要使用工具(Eclipse)。

 

我们打开Eclipse,然后创建一个Java工程。在src文件夹下编写book.xml和book.dtd文件。这时,如果XML文件的编写不符合DTD约束,Eclipse就会给我们错误提示。

外部引入和内部嵌入的特点:

① 如果一个DTD文件需要被多个XML文件引用,为了避免在每个XML中都添加相同的一段DTD定义语句,我们可以将其放在一个单独的DTD文件中,采用外部引入的方式对XML文档进行管理。这样不仅便于管理和维护DTD定义,还可以使多个XML文档共享使用一个DTD文件。

② 内部嵌入比较直观,DTD约束和XML元素使用都在同一个XML文件中,编写代码、编辑DTD约束比较方便。

3、DTD语法

DTD的结构一般由元素类型定义、属性定义、实体定义、记号(notation)定义等构成,一个典型的DTD会把将来要创建的XML文档的元素结构、属性类型、实体引用等预先进行定义。

(1)元素定义

元素(标记)是XML文档的基本组成部分,在DTD定义中,每一条<!ELEMENT…>语句用于定义一个元素,其基本的语法格式如下所示:

<!ELEMENT 元素名称 元素内容>

在上面的语法格式中,包含了“元素名称”和“元素内容”。

  • 元素名称:是自定义的名称,它用于定义被约束XML文档中的元素(标记、标签);
  • 元素内容:是对元素包含内容的声明,包括数据类型和符号两部分。XML的元素分为简单元素和复杂元素两种,DTD中元素的定义共有5种形式,其中前三种是简单元素的类型:

① (#PCDATA):普通字符串,PCDATA是可解析字符数据(Parsed Character Data),要有括号()。

② EMPTY:空元素,既不包含字符数据,也不包含子元素。如果在文档中元素本身已经表明了明确的含义,就可以在DTD中用关键字EMPTY表明空元素。例如, <!ELEMENT br EMPTY> ,其中br是一个没有内容的空元素。不用括号()。

③ ANY:任何元素,表示该元素可以包含任何的字符数据和子元素。例如, <!ELEMENT 联系人 ANY> 表示联系人可以包含任何形式的内容。但在实际开发中,应该尽量避免使用ANY,因为除了根元素外,其他使用ANY的元素都将失去DTD对XML文档的约束效果。不用括号()。

④ 子元素:可以包含其他元素。通常用一对括号()将元素中要嵌套的一组子元素括起来,中间使用逗号“,”分隔,表示这些子元素按照这个顺序在XML中出现。例如, <!ELEMENT 书 (书名,作者,售价)> 表示元素书中要嵌套书名、作者、售价子元素,这些子元素只能出现一次,并且按照这个顺序编写。

如果一个子元素要出现多次,我们可以使用一些符号来表示次数:

+(一次或者多次),?(零次或者一次)、*(零次、一次或者多次)

例如, <!ELEMENT 书 (书名+,作者?,售价*)> 

还有一个符号非常重要----竖线“|”,表示“或”的意思,如果几个子元素之间使用竖线“|”隔开,表示这几个子元素只能出现其中的任意一个。例如,<!ELEMENT 书 (书名|作者|售价)>。

⑤ 混合内容:既可以包含字符数据,也可以包含子元素。混合内容必须被定义零个或多个,例如, <!ELEMENT 书 (#PCDATA|书名)*> 表示书中嵌套的子元素书名包含零个或多个,并且书名是字符串文本格式。要有括号()。

(2)属性定义

在定义元素的同时,还可以为元素定义属性。DTD属性定义的基本语法格式:

<!ATTLIST 元素名称
属性名称1 属性类型 设置说明
属性名称2 属性类型 设置说明
……
> 

在上面属性定义的语法格式中,“元素名称”是属性所属元素的名字,“属性名称”是属性的名称,“属性类型”则是用来指定该属性是属于哪种类型,“设置说明”用来说明该属性是否必须出现。关于“属性类型”和“设置说明”的相关讲解,具体如下。

① 属性类型

在DTD中定义元素的属性时,有10种属性类型可以选择,常见的几种属性类型介绍如下。

序号

属性类型

含义

1

CDATA

纯文本字符串。如果出现特殊字符,需要转义。

2

枚举类型

枚举列表中的一个值,用竖线“|”隔开,每次只能选择一个。

3

ID

唯一的id,只能是字母或下划线开始。

4

IDREF

另外一个元素的id,【reference:引用,参考】

5

IDREFS

其他id的列表,中间用空格隔开

6

NMTOKEN

合法的XML名称,Name Token,名称记号,它表示由一个或多个字母、数字、句点、连字号或下划线所组成的一个名称。当encoding编码是中文码时,可以使用中文。

7

NMTOKENS

合法的XML名称的列表,中间用空格隔开

8

NOTATION

记号的名称(后面还有单独讲解)

9

ENTITY

一个已定义的实体,预定义实体/自定义实体(后面还有单独讲解)

10

ENTITIES

一个已定义的实体的列表

② 设置说明

定义元素的属性时,有4种设置说明可以选择,具体如下:

序号

设置说明

含义

1

#REQUIRED

属性必须存在

2

#IMPLIED

属性可有可无

3

#FIXED value

表示一个固定值,#FIXED "ABC",表示属性必须是这个固定值"ABC"

4

默认值

表示一个默认值,如果元素不写该属性,该属性自动被设置为默认值。如果写了该属性,就是用属性的实际值。

例如:

① DTD定义: <!ATTLIST 作者 分类 (AA|BB|CC) #REQUIRED> 

XML文件: <作者 分类="AA">传智播客</作者> 

② DTD定义: <!ATTLIST 书名 编号 ID #REQUIRED> 

XML文件: <书名 编号="a1">JavaWeb程序开发入门</书名> 

③ DTD定义: <!ATTLIST 售价 单位 CDATA #FIXED "元"> 

XML文件: <售价 单位="元">60</售价> 

④ CDATA 案例----“家庭”

<?xml version="1.0" encoding="UTF-8"?>
<!--包含两个元素,人至少出现1次,家电可以出现0/1/多次,这两个元素都被定义为空类型 -->
<!DOCTYPE 家庭 [
    <!ELEMENT 家庭 (人+,家电*)>
    <!ELEMENT 人 EMPTY>
    <!ELEMENT 家电 EMPTY>
    <!--开始定义属性-->
    <!ATTLIST 人
        名字 CDATA #REQUIRED 
        年龄 CDATA #REQUIRED
        爱好 CDATA #IMPLIED>
    <!ATTLIST 家电
        名称 CDATA #REQUIRED 
        数量 CDATA #REQUIRED
        说明 CDATA #IMPLIED>
]>
<!--DTD部分结束 -->
<家庭>
    <人 名字="张三" 年龄="25" />
    <人 名字="李四" 年龄="38" 爱好="踢足球" />
    <家电 名称="彩电" 数量="三" />
</家庭>

⑤ ID案例----“公司”

<?xml version="1.0" encoding="UTF-8"?>
<!--一个内部DTD的示例,首先写根元素“公司” -->
<!DOCTYPE 公司 [
    <!ELEMENT 公司 ANY>
    <!ELEMENT 公司职员 (#PCDATA)>
    <!ATTLIST 公司职员
        编号 ID #REQUIRED
        姓名 CDATA #REQUIRED
    >
]>
<!--DTD部分结束 -->
<!--编号被定义为ID,因此是不可重复的 -->
<公司>
    <公司职员 姓名="张三" 编号="Z001" />
    <公司职员 姓名="李四" 编号="Z002" />
</公司>

⑥ IDREF/IDREFS案例----“家庭”

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 家庭[
    <!ELEMENT 家庭 (人+)>
    <!ELEMENT 人 EMPTY>
    <!ATTLIST 人
        relID ID #REQUIRED
        parentID IDREFS #IMPLIED
        name CDATA #REQUIRED>
]>
<!--DTD部分结束 -->
<家庭>
    <人 relID="P_1" name="爸爸" />
    <人 relID="P_2" name="妈妈" />
    <!-- 属性值引用已经定义的ID值,方法是把那个元素的ID标识符作为该元素的取值,不同的引用之间用空格分开 -->
    <人 relID="P_3" parentID="P_1 P_2" name="儿子" />
</家庭>

⑦ NMTOKEN案例----“八阵图”

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE poems [
    <!ELEMENT poems (title, content)>
    <!ELEMENT title (#PCDATA)>
    <!--定义属性,NMTOKEN说明该属性由一个或多个字母、数字、句点、连字号或下划线所组成的一个名称,当encoding编码是中文码时,可以使用中文-->
    <!ATTLIST title    author NMTOKEN #REQUIRED>
    <!ELEMENT content (#PCDATA)>
]> 
<poems>
    <title author="杜甫">八阵图</title>
    <content>
          功盖三分国,名成八阵图,
          江流石不转,遗恨失吞吴.
    </content>
</poems>

注意,如果是NMTOKENS,属性值能由多个NMTOKEN构成,每个NMTOKEN之间用空格隔开。

案例二:电视节目表 DTD

<!DOCTYPE TVSCHEDULE [
    <!ELEMENT TVSCHEDULE (CHANNEL+)>
    <!ELEMENT CHANNEL (BANNER,DAY+)>
    <!ELEMENT BANNER (#PCDATA)>
    <!ELEMENT DAY (DATE,(HOLIDAY|PROGRAMSLOT+)+)>
    <!ELEMENT HOLIDAY (#PCDATA)>
    <!ELEMENT DATE (#PCDATA)>
    <!ELEMENT PROGRAMSLOT (TIME,TITLE,DESCRIPTION?)>
    <!ELEMENT TIME (#PCDATA)>
    <!ELEMENT TITLE (#PCDATA)> 
    <!ELEMENT DESCRIPTION (#PCDATA)>
    
    <!ATTLIST TVSCHEDULE NAME CDATA #REQUIRED>
    <!ATTLIST CHANNEL CHAN CDATA #REQUIRED>
    <!ATTLIST PROGRAMSLOT VTR CDATA #IMPLIED>
    <!ATTLIST TITLE RATING CDATA #IMPLIED>
    <!ATTLIST TITLE LANGUAGE CDATA #IMPLIED>
]>

根据上面提供的DTD文档定义格式,写出XML文件内容。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE TVSCHEDULE [
……
]>
<TVSCHEDULE NAME="CCTV">
    <CHANNEL CHAN="CCTV-1">
        <BANNER>XXXX</BANNER>
        <DAY>
            <DATE>2019-03-11</DATE>
            <HOLIDAY>XXXX</HOLIDAY>
        </DAY>
        <DAY>
            <DATE>2019-03-12</DATE>
            <PROGRAMSLOT VTR="XXXX">
                <TIME>1900</TIME>
                <TITLE LANGUAGE="TTTT">XXXX</TITLE>
                <DESCRIPTION>XXXX</DESCRIPTION>
            </PROGRAMSLOT>
        </DAY>
    </CHANNEL>
</TVSCHEDULE>

(3)实体定义

有时候需要在各个文档中调用同样的内容,例如公司名称、版权声明等,为了避免重复输入这些内容。可以通过<!ENTITY……>语句定义一个表示这些内容的实体,然后在各个文档中引用实体名代替它所表示的内容。类似于定义了一个常量,在其他地方多次使用这个常量。

实体可分为两种类型,分别是引用实体参数实体

① 引用实体

引用实体的定义格式有两种:

<!ENTITY 实体名称 "实体内容">
<!ENTITY 实体名称 SYSTEM "外部XML文档的URL">

使用引用实体的方式为:

&实体名称;

写法非常类似于转义字符的使用:空格  &nbsp;

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE 书架 [
    <!ELEMENT 书架 (书+)>
    <!ELEMENT 书 (书名,作者,售价)>
    <!ELEMENT 书名 (#PCDATA)>
    <!ELEMENT 作者 (#PCDATA)>
    <!ELEMENT 售价 (#PCDATA)>
    <!ENTITY zhs "张三">
]>
<书架>
    <>
        <书名>JavaWeb程序开发入门</书名>
        <作者>&zhs;</作者>
        <售价>60</售价>
    </>
    <>
        <书名>JavaWeb从入门到精通</书名>
        <作者>明日科技</作者>
        <售价>80</售价>
    </>
</书架>

 

② 参数实体

参数实体只能被DTD文件自身使用,语法格式:

<!ENTITY % 实体名称 "实体内容">

注意:ENTITY、%、实体名称、"实体内容"之间各有一个空格。

引用参数实体的方式为:

%实体名称;

在实体名称之前增加百分号,在其后增加分号。百分号与实体名称之间没有空格。

来看一下下面的DTD定义:

<!ENTITY % na "书名,作者">
<!ELEMENT 书架 (书+)>
<!ELEMENT 书 (%na;,售价)>
<!ELEMENT 书名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>

在第三行中引用了第一行中定义的参数实体na来表示"书名,作者"。

注意:内部 DTD 子集中的标记声明内不允许使用参数实体引用,只能在外部DTD文件中使用参数实体。

参数实体不仅可以简化元素中定义的相同内容,还可以简化属性的定义,具体实例参见课本27页。

(4)NOTATION记号定义(或符号定义)

在现实中,很多数据是无法用XML表示的,例如:声音、图像、影像、程序等,对于这些数据,XML处理器通常都不支持。

我们就需要使用记号声明,为非XML数据描述一种可能的格式,或者指定一个外部的处理程序。

我们使用<!NOTATION…>语句定义,有两种语法格式,分别定义普通记号和公用记号:

记号类型

定义语法

普通记号

<!NOTATION 符号名 SYSTEM "记号值">

公用记号

<!NOTATION 符号名 PUBLIC "公用符号标识名" "记号值">

记号值通常有两种形式:

① MIME:通用MIME类型的文件总是由相应的程序负责处理。

<!NOTATION gif SYSTEM "image/gif">

② 外部程序所在路径:直接指定某个外部程序负责处理XML文档中的外部数据。

<!NOTATION gif SYSTEM "iexplore.exe">

五、Schema约束

同DTD一样,XML Schema(XML Schema Definition,XSD,XML Schema定义)也是一种用于定义和描述XML文档结构与内容的模式语言,它的出现克服了DTD 的局限性。接下来,通过XML Schema与DTD的比较,将XML Schema所具有的一些显著优点进行列举,具体如下:

(1)DTD采用的是非XML语法格式,缺乏对文档结构、元素、数据类型等全面的描述。而XML Schema采用的是XML语法格式,而且它本身也是一种XML文档,因此,XML Schema语法格式比DTD更好理解。

(2)XML有非常高的合法性要求,虽然DTD和XML Schema都用于对XML文档进行描述,都被用作验证XML合法性的基础。但是,DTD本身合法性的验证必须采用另外一套机制,而XML Schema则采用与XML文档相同的合法性验证机制。

(3)XML Schema对名称空间支持得非常好,而DTD几乎不支持名称空间。

(4)DTD支持的数据类型非常有限。例如,DTD可以指定元素中必须包含字符文本(PCDATA),但无法指定元素中必须包含非负整数,而XML Schema比DTD支持更多的数据类型,包括用户自定义的数据类型。

(5)DTD定义约束的能力非常有限,无法对XML实例文档作出更细致的语义限制,例如,无法很好地指定一个元素中的某个子元素必须出现7-12次;而XML Schema定义约束的能力非常强大,可以对XML实例文档作出细致的语义限制。

通过上面的比较可以发现,XML Schema的功能比DTD强大很多,但相应的语法也比DTD复杂很多。Schema是基于XML编写的,XML Schema约束文件本身就是一个XML文档(文件后缀名为.xsd),文件内的代码要符合XML语法规范。

【复习】DTD文件语法:

<!ELEMENT 元素名称 元素内容>
<!ATTLIST 元素名称
属性名称1 属性类型 设置说明
属性名称2 属性类型 设置说明
……
>

那么,我们使用Eclispe向导新建一个“XML Schema File”,扩展名是.xsd,名为NewXMLSchema.xsd,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://www.example.org/NewXMLSchema"
xmlns:tns="http://www.example.org/NewXMLSchema"
elementFormDefault="qualified">
</schema>

上面的代码中:

第1行是文档声明,与xml文档声明一样。

第2行中以<schema>为根元素,表示模式定义的开始。

  • 属性xmlns="http://www.w3.org/2001/XMLSchema"表示使用了标准的命名空间(ns,namespace),与此命名空间相关联的 URI 是 Schema 的语言定义(Schema Language Definition),其标准值是 http://www.w3.org/2001/XMLSchema。表示当前文件是一个XML Schema约束文件;
  • 属性targetNamespace="http://www.example.org/NewXMLSchema"表示的是如果要使用这个Schema约束文件,可以直接通过这个地址引入约束文件;
  • 属性xmlns:tns="http://www.example.org/NewXMLSchema"表示这个Schema约束文件他本身的名称空间;
  • 属性elementFormDefault="qualified"表示是质量良好的。

一个XML只能引入一个DTD约束文档,但是一个XML中可以有多个Schema约束文档,多个Schema使用名称空间区分(类似于Java 的类是按照Java包名来区分的是一个道理)。

1、名称空间

一个XML文档可以引入多个Schema约束文档,但是,由于约束文档中的元素或属性都是自定义的,因此,在XML文档中,极有可能出现代表不同含义的同名元素或属性,导致名称发生冲突。为此,在XML文档中,提供了名称空间,它可以唯一标识一个元素或者属性。这就好比咱们系有两个同名的同学,如果老师要找那个同学,就得给他们的名字前面加个前缀,XXX班的某某某。这个“XXX班”就相当于一个名称空间。

在使用名称空间时,首先必须声明名称空间。名称空间的声明就是在XML实例文档中为某个模式文档的名称空间指定一个临时的简写名称(起个别名),它通过一系列的保留属性来声明,这种属性的名字必须是以“xmlns”或者以“xmlns:”作为开始。它与其它任何XML属性一样,都可以通过直接或者使用默认的方式给出。

名称空间声明的语法格式如下所示:

<元素名称 xmlns:prefixname="URI">

在上述语法格式中,元素名称指的是在哪一个元素上声明名称空间,在这个元素上声明的名称空间适用于声明它的元素和属性,以及该元素中嵌套的所有元素及其属性。xmlns:prefixname指的是该元素的属性名,它所对应的值是一个URI引用,用来标识该名称空间的名称。

我们来修改book.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<xs:书架 xmlns:xs="http://www.example.org/book/schema"
xmlns="http://www.w3.org/2001/XMLSchema-instance">
    <xs:>
        <xs:书名>JavaWeb程序开发入门</xs:书名>
        <xs:作者>传智播客</xs:作者>
        <xs:售价>60</xs:售价>
    </xs:书>
</xs:书架>

名称空间的使用就是将一个前缀(xs)绑定到代表某个名称空间的URI(http://www.example.org/book/schema)上。

然后将前缀添加到元素名称前面来说明该元素属于哪个Schema文档。

如果一个XML文档有很多元素,而且这些元素都在同一个名称空间,这时,给每个元素名称都添加一个前缀将是一件非常烦琐的事情。

这时,可以使用默认的名称空间,默认名称空间声明时不需要加“别名”,使用这些元素时,也不用加前缀。

<?xml version="1.0" encoding="UTF-8"?>
<xs:书架 xmlns:xs=http://www.example.org/book/schema>
    <xs:>
        <xs:书名>JavaWeb程序开发入门</xs:书名>
        <xs:作者>传智播客</xs:作者>
        <xs:售价>60</xs:售价>
    </xs:>
</xs:书架>

2、引入Schema文档

如果想通过XML Schema文件对某个XML文档进行约束,必须将XML文档与Schema文件进行关联。在XML文档中引入Schema文件有两种方式:

(1)使用名称空间引入Schema

上面的书架book.xml文件对应的XML Schema约束文件book.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org/book" 
    xmlns:tns="http://www.example.org/book"
    elementFormDefault="qualified">
    <element name="书架">
        <complexType>
            <sequence>
                <element name="书">
                    <complexType>
                        <sequence>
                            <element name="书名" type="string" />
                            <element name="作者" type="string" />
                            <element name="售价" type="string" />
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>

那么,我们需要在XML文档中的根节点中使用schemaLocation属性来指定Schema文件。schemaLocation属性有两个值:

第一个值是需要使用的名称空间;

第二个值是供命名空间使用的XML Schema文件的路径;

两者之间用空格分隔

我们来创建book.xml文件时,可以使用Eclipse的新建XML文件向导。

 

 

修改生成的book.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<书架 xmlns="http://www.example.org/book" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.example.org/book book.xsd ">
    <>
        <书名>JavaWeb程序开发入门</书名>
        <作者>传智播客</作者>
        <售价>60</售价>
    </>
</书架>

如果要使用名称空间引入多个XML Schema文档。修改book.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<书架 xmlns="http://www.example.org/book" 
    xmlns:demo="http://www.example.org/demo"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.example.org/book book.xsd
                        http://www.example.org/demo demo.xsd ">
    <>
        <书名>JavaWeb程序开发入门</书名>
        <作者>传智播客</作者>
        <售价 demo:类型="元">60</售价>
    </>
</书架>

(2)不使用名称空间引入Schema

如果book.xsd与引用它的XML文件位于同一个目录中,我们可以不使用名称空间来引入Schema,book.xsd中不需要定义targetNamespace(目标名称空间)和xmlns:tns(所在名称空间)了,book.xsd代码:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    elementFormDefault="qualified">
    <element name="书架">
        <complexType>
            <sequence>
                <element name="书">
                    <complexType>
                        <sequence>
                            <element name="书名" type="string" />
                            <element name="作者" type="string" />
                            <element name="售价" type="string" />
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>

如果这时再用Eclipse向导来创建基于book.xsd的XML文件,就是如下图所示了。

 

修改book.xml文件,在book.xml中也不需要指定默认名称空间了。

<?xml version="1.0" encoding="UTF-8"?>
<书架 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="book.xsd">
    <>
        <书名>JavaWeb程序开发入门</书名>
        <作者>传智播客</作者>
        <售价>60</售价>
    </>
</书架>

noNamespaceSchemaLocation是一个在标准名称空间http://www.w3.org/2001/XMLSchema-instance里面定义好的属性,用于指定book.xsd文档位置。

3、Schema语法

(1)元素定义

Schema和DTD一样,都可以定义XML文档中的元素。在Schema文档中,元素定义的语法格式如下所示:

<element name="xxx" type="yyy" />

在上面的语法格式中,element用于声明一个元素,这里使用默认名称空间,所以省去了前缀xs:,xxx指的是元素的名称,yyy指元素的数据类型。在XML Schema中有很多内建的数据类型,其中最常用的有以下几种(数据类型也是,省去了前缀xs:):

  • string:表示字符串类型
  • decimal:表示小数类型
  • integer:表示整数类型
  • boolean:表示布尔类型
  • date:表示日期类型
  • time:表示时间类型
  • positiveInteger:表示正整数。

接下来看一个XML的示例代码:

<lastname>Smith</lastname>
<age>28</age>
<birthday>1990-03-12</birthday>

上面的XML示例代码中,定义了三个元素,这三个元素对应的Schema定义如下所示:

<element name="lastname" type="string" />
<element name="age" type="integer" />
<element name="birthday" type="date" />

(2)属性定义

在Schema文档中,属性定义的语法格式如下所示:

<attribute name="xxx" type="yyy" />

在上面的语法格式中,xxx指的是属性名称,yyy指的是属性的数据类型。其中,属性的常用数据类型与元素相同,都使用的是XML Schema中内建的数据类型。

了解了属性的定义方式,接下来,看一个XML的简单例子,具体示例如下所示:

<lastname lang="EN">Smith</lastname>

在上面的元素定义中,属性名称是lang,属性值的类型是字符串类型,因此,对应的Schema中的属性定义如下:

<attribute name="lang" type="string" />

(3)简单类型<simpleType>

只包含字符数据的元素都是简单类型,使用<simpleType>元素来定义。

如果想对现有元素内容的类型进行进一步限制,则需要使用<restriction>元素(restriction,限制,限定)

① minInclusive和maxInclusive元素对值的限定(最值约束)

如果要定义一个员工的年龄时,年龄要求在18-58周岁之间,我们需要对年龄age这个元素进行限定,具体Schema代码如下:

<element name="age">
    <simpleType>
        <restriction base="integer">
            <minInclusive value="18" />
            <maxInclusive value="58" />
        </restriction>
    </simpleType>
</element>

上面的代码表示,元素age是integer类型,最小值包括18,最大值包括58。

正确XML: <age>20</age> 

另外,类似的还有两个:

minExclusive:定义数值的下限。所允许的值必须大于此值(不包括此值)。

maxExclusive:定义数值的上限。所允许的值必须小于此值(不包括此值)。

② enumeration元素对于一组值的限定(枚举约束)

如果希望XML中的某个元素的内容限制在一组值中的一个,我们可以使用枚举约束,例如,要限定一个元素名为car的元素,它可接收的值只有Benz、BMW、Audi,具体Schema代码如下:

<element name="car">
    <simpleType>
        <restriction base="string">
            <enumeration value="Benz" />
            <enumeration value="BMW" />
            <enumeration value="Audi" />
        </restriction>
    </simpleType>
</element>

正确XML:<car>Benz</car>

③ pattern元素对于一系列值的限定(模式约束)

常用正则表达式

[abc]     a、b 或 c(简单类)

[^abc]   任何字符,除了 a、b 或 c(否定)

[a-zA-Z]  a到 z 或 A 到 Z,两头的字母包括在内(范围)

X?   X,一次或零次

X*   X,零次或多次

X+   X,一次或多次

X{n}    X,恰好 n 次

X{n,}    X,至少 n 次

X{n,m}   X,至少 n 次,但是不超过 m 次

如果希望把XML中的某个元素的内容限制在一系列可使用的数字和字母中,可以使用模式约束。例如,要定义一个带有限定的元素letter,要求可接受的值只能是a-z其中的一个,具体Schema代码如下:

<element name="letter">
    <simpleType>
        <restriction base="string">
            <pattern value="[a-z]" />
        </restriction>
    </simpleType>
</element>

正确XML: <letter>a</letter> 

④ whiteSpace元素对空白字符的限定(空格约束)

在XML文档中,空白字符比较特殊,如果需要对空白字符进行处理,可以使用whiteSpace进行约束。whiteSpace元素有三个属性值:

  • preserve:不对元素中的任何空白字符进行处理(保持原状)
  • replace:移除所有空白字符
  • collapse:将所有空白字符缩减为一个单一字符

例如,对于地址address元素内容中的空白字符不做任何处理,具体Schema代码如下:

<element name="address">
    <simpleType>
        <restriction base="string">
            <whiteSpace value="preserve" />
        </restriction>
    </simpleType>
</element>

正确XML: <address>abc def ghi</address> 

(4)复杂类型<complexType>

除简单类型之外的其他类型都是复杂类型,在定义复杂类型时,需要使用<complexType>元素来定义。复杂类型的元素可以包含子元素和属性,这样的元素称为复合元素。在定义复合元素时,如果元素的开始标记和结束标记之间只包含字符数据内容,那么这样的内容是简易内容,需要使用<simpleContent>元素来定义。反之,元素的内容都是复杂内容,需要使用<complexContent>元素来定义。复合元素有4种基本类型:

① 空元素

这里的空元素指的是不包含内容,只包含属性的元素,具体示例如下:

<product prodid="1345" />

在上面的元素定义中,没有定义元素product的内容,这时,空元素在XML Schema文档中对应的定义方式如下所示:

<element nane="product">
    <complexType>
        <attribute name="prodid" type="positiveInteger" />
    </complexType>
</element>

属性类型positiveInteger表示正整数。

② 包含其他元素的元素

XML文档中包含其他元素的元素,例如下面的示例代码:

<person>
    <firstname>John</firstname>
    <lastname>Smith</lastname>
</person>

在上面的示例代码中,元素person 嵌套了两个元素,分别是firstname和lastname。这时,在Schema文档中对应的定义方式如下所示:

<element name="person">
    <complexType>
        <sequence>
            <element name="firstname" type="string" />
            <element name="lastname" type="string" />
        </sequence>
    </complexType>
</element>

③ 仅包含文本的元素----简易内容

对于仅包含文本的复合元素,需要使用simpleContent元素来添加内容。在使用简易内容时,必须在simpleContent元素内定义扩展或限定,这时,需要使用extension 或restriction元素来扩展或限制元素的基本简易类型。请看-个XML的简易例子,其中,"shoesize"仅包含文本,具体示例如下:

<shoesize country="france">35</shoesize>

在上面的例子中元素shoesize 包含属性以及元素内容,针对这种仅包含文本的元素,需要使用<extension>来对元素的类型进行扩展,在Schema文档中对应的定义方式如下所示:

<element name="shoesize">
    <complexType>
        <simpleContent>
            <extension base="integer">
                <attribute name="country" type="string" />
            </extension>
        </simpleContent>
    </complexType>
</element>

④ 包含元素和文本的元素

在XML文档中,某些元素经常需要包含文本以及其他元素,例如,下面的这段XML文档:

<letter>
    Dear Mr.<name>John smith</name>.
    Your order<orderid>1032</orderid>
    will be shipped on<shipdate>2001-07-13</shipdate>
</letter>

上面的这段XML文档,在Schema文档中对应的定义方式如下所示:

<element name="letter">
    <complexType mixed="true">
        <sequence>
            <element name="name" type="string" />
            <element name="orderid" type="positiveInteger" />
            <element name="shipdate" type="date" />
        </sequence>
    </complexType>
</element>

需要注意的是,为了使字符数据可以出现在letter元素的子元素之间,使用了mixed属性,该属性是用来规定是否允许字符数据出现在复杂类型的子元素之间,默认情况下mixed的值是false。

最后,整个person.xsd代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.example.org/person/schema"
    targetNamespace="http://www.example.org/person/schema" 
    elementFormDefault="qualified">
    <xs:element name="person">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="firstname" type="xs:string" />
                <xs:element name="lastname" type="xs:string" />
                <xs:element name="age">
                    <xs:simpleType>
                        <xs:restriction base="xs:integer">
                            <xs:minInclusive value="18" />
                            <xs:maxInclusive value="58" />
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>
                <xs:element name="birthday" type="xs:date" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

整个person.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<person xmlns="http://www.example.org/person/schema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.example.org/person/schema person.xsd">
    <firstname>John</firstname>
    <lastname>Smith</lastname>
    <age>18</age>
    <birthday>1990-03-12</birthday>
</person>

【DTD约束形式】

<!ELEMENT person (firstname, lastname, age, birthday)>
<!ELEMENT firstname (#PCDATA)>
<!ELEMENT lastname (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT birthday (#PCDATA)>

 

posted @ 2022-08-30 21:59  熊猫Panda先生  阅读(644)  评论(0编辑  收藏  举报