咱要摒弃旧技术,就得了解旧技术(当然这里指的新旧是依然有部分联系的哈,不是截然不同的),我认为原因有二:
- 旧的技术有时候不是被全盘抛弃的,新的技术有时候(大多时候)是脱胎于旧技术的,所以了解旧的技术并不算是浪费时间。
- 了解了旧技术之后,你就会发现痛点,哪里不好用,哪里限制太死,这个时候再学习新技术,对比理解一方面可以加深新技术革新部分的记忆,保持印象深刻。另一方面又可以对这个技术的整体有个认知。
说了以上这么多废话,就是想表达,在了解Schema之前,我得搞明白DTD的工作原理。
DTD概述
概念用途
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。说白了就是对XML文档结构进行定义,检查XML文档到底合不合法。好处有如下几点(3C上如下所说,我略微理解下):
- 通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。说白了就是有了这个东西在,你的任何一个庞大的XML在给别人看之前,都可以先给人家看看DTD,这样给一个目录让人家阅读起来省事儿。
- 通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。说白了就是几个团队做事情的时候需要交换XML数据,只要使用统一标准的DTD,大家各自的应用程序都能通畅的交换数据。
- 而您的应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据。说白了就是从别人那儿获取的数据我得知道符合不符合格式呀,就比如给运维丢一个xml让发上去,人家拿着DTD一验证没事儿,就省去了肉眼人工检查了。
- 您还可以使用 DTD 来验证您自身的数据。说白了就是自己写完一个XML,没准儿哪儿丢一个或者多一个闭合尖叫号(我就犯过这错误),可以用DTD来自己验证一遍格式有没有问题。
使用方式
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
直接声明在XML中
其实类似于HTML引入CSS和JS,你可以选择直接在html里边用style。DTD也可以直接声明到到XML中。
<!DOCTYPE 根元素 [元素声明]> //通过这样的方式声明
这里举一个例子如下,要注意的是,中括号内部要将各个元素放进去,收尾要加中括号哦。
<?xml version="1.0"?> <!DOCTYPE note [ <!ELEMENT note (name,sex,age,describe)> //note 元素有四个元素:"name,sex,age,describe" <!ELEMENT name (#PCDATA)> //元素为 "#PCDATA" 类型 <!ELEMENT sex (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ELEMENT describe (#PCDATA)> ]> <note> <name>茂神</name> <sex>男</sex> <age>24</age> <describe>茂神最帅了!</describe> </note>
作为文件引入XML中
大多数时候其实我们还是采取以下这种方式,这种方式让XML相当干净整洁,只要根元素名对应上就可以了。
<!DOCTYPE 根元素 SYSTEM "文件名">
还是不害臊的举上边那个例子吧
<?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <name>茂神</name> <sex>男</sex> <age>24</age> <describe>茂神最帅了!</describe> </note>
然后note.dtd文件内容呢如下所示:
<!ELEMENT note (name,sex,age,describe)> //note 元素有四个元素:"name,sex,age,describe" <!ELEMENT name (#PCDATA)> //元素为 "#PCDATA" 类型 <!ELEMENT sex (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ELEMENT describe (#PCDATA)>
很显然,就是把中括号里边的东西都放到这个文件里来了。
组成结构
那么DTD要想描述XML,就得知道XML有哪些组成模块,这样才能对的上,所有的XML均由以下几部分构成:元素 、属性 、实体 、PCDATA 、CDATA 其中后边三种都是关于文本的,私以为算不上组成结构,应该是对数据内容的描述。元素和属性在上一篇里已经详细讨论过了:传送门 https://blog.csdn.net/sinat_33087001/article/details/80874849 这里重点说一下后边三种。
实体
实体是用来定义普通文本的变量。实体引用是对实体的引用。就好比说空格是一个普通文本,那么nbsp算作一个实体,那么
就算作一个实体引用。XML预置的5种实体和实体引用:
PCDATA
PCDATA 的意思是被解析的字符数据(parsed character data)。可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。我的理解就是元素的类型,元素中间的文本内容将被解析器检查实体以及标记,文本中的标签会被当作标记来处理,而实体会被展开。怎么理解呢,举个例子:
<?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <descripe><font color='red'>茂 神</descripe> </note>
当设定为PCDATA的时候descripe标签内容被解析后,将会显示为:茂 神,也就是说标签内部的标记都被读取了。
CDATA
CDATA 的意思是字符数据(character data)。CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。其实也算是一种约束,区别于PCDATA来看的,怎么理解呢,还举上边那个个例子:
<?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <descripe><font color='red'>茂 神</descripe> </note>
当设定为CDATA的时候descripe标签内容被解析后,将会显示为:
<font color='red'>茂 神
也就是说标签内部的标记都被原封不动的显示出来了。
DTD使用实战
元素
在一个 DTD 中,元素通过元素声明来进行声明。
元素声明
元素声明有两种方式,区别是最后一位是类别或者元素内容
<!ELEMENT 元素名称 类别> <!ELEMENT 元素名称 (元素内容)>
元素使用
类型和结构界定
通过第一种方式声明的:<!ELEMENT 元素名称 类别>
,这里类别为下边左一栏标识的
类别 | 举例 | 描述 |
EMPTY | <!ELEMENT br EMPTY> |
例如html里的换行符<br/> 空元素,通过EMPTY来标识 |
(#PCDATA) | <!ELEMENT name (#PCDATA)> |
只有 PCDATA 的元素通过圆括号中的 #PCDATA 进行声明 |
ANY | <!ELEMENT 元素名称 ANY> |
例如<!ELEMENT note ANY> 根节点可以包括任意结构 |
(子元素名称 1) | <!ELEMENT 元素名称 (子元素名称 1,子元素名称 2,.)> |
带一个或多个子元素的元素通过圆括号中的子元素名进行声明 |
要特别注意:当声明带有一个或多个子元素的元素,这些子元素必须按照相同的顺序出现在文档中。
出现次数界定
通过第二种方式声明的:<!ELEMENT 元素名称 (元素内容)>
,这里类别为下边左一栏标识的
类别 | 举例 | 描述 |
(子元素名称+) | <!ELEMENT note (message+)> |
声明最少出现一次的元素,message子元素至少在note元素里出现一次 |
(子元素名称*) | <!ELEMENT note (message*)> |
声明出现零次或多次的元素,message子元素可在note元素里出现零次或多次。 |
(子元素名称?) | <!ELEMENT note (message?)> |
声明出现零次或一次的元素,message子元素可在note元素里出现零次或一次。 |
(子元素1|子元素2) | <!ELEMENT note (name,(sex |age))> |
非既,note元素必须包含name子元素,以及sex或age两个元素之一 |
混合型内容
<!ELEMENT note (#PCDATA|to|from|header|message)*>
这个就比较厉害了,类型和元素内容的混合使用,并且非既括号中的内容可以出现零次或多次。
属性
属性声明
在 DTD 中,属性通过 ATTLIST 声明来进行声明。
<!ATTLIST 元素名称 属性名称 属性类型 默认值> //语法结构 <!ATTLIST payment type CDATA "check"> //实例 <payment type="check" /> //应用到xml中
属性使用
属性的类型和默认值都有一些备选项。
属性类型备选
看来属性类型没有PCDATA,也对,没有要解析的标记,直接用CDATA方便些。
属性默认值备选
下面解释下四种植的含义:
- 第一种值:直接给属性一个默认值,如果你没赋值,就用我默认的。
- 第二种#REQUIRED:表示你必须指定属性值,没指定就非法。
- 第三种#IMPLIED:表示无所谓,你指不指定属性值都合法。
- 第四种FIXED value:表示属性值你必须得有,而且还必须是我给出的固定的。也就是说我是规则制定者,要求方,你(XML的编写者)必须按照我的要求来做。
列举属性值
<!ATTLIST 元素名称 属性名称 (en1|en2|..) 默认值> //语法 <!ATTLIST payment type (check|cash) "cash"> //dtd举例
如果采用了这种列举属性值的语法,那么结果为如下的二选一
<payment type="check" />
或者
<payment type="cash" />
这里有一个疑问,使用列举属性值语法,属性值的属性类型如何判断呢?
实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。实体引用是对实体的引用。实体可在内部或外部进行声明。
内部实体声明
声明语法
<!ENTITY 实体名称 "实体的值"> //语法
DTD举例
<!ENTITY writer "田茂林"> <!ENTITY copyright "Copyright maolintian">
XML中使用
<author>&writer;©right;</author> //当解析的时候就会展开实体
注释: 一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)。这里W3C表述应该有误,应该是一个实体引用由这三部分组成,参考MSDN得出的结论,这个困扰了一些时间。
外部实体声明
声明语法
<!ENTITY 实体名称 SYSTEM "URI/URL">
DTD 举例
//该url地址下包含文字:田茂林 <!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd"> //该url地址下包含文字:Copyright maolintian <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
XML 中使用
<author>&writer;©right;</author>
DTD实战
分析一下DTD的实战举例
<!DOCTYPE tvschedule [ <!ELEMENT tvschedule (channel+)> //电视节目根元素包括多个频道子元素 <!ELEMENT channel (banner,day+)> //每个频道子元素都包含一个主题和多个day元素 <!ELEMENT banner (#PCDATA)> //每个主题都是PCDATA类型的 <!ELEMENT day (date,(holiday|programslot+)+)> //每个day元素又包括一个data日期元素,和至少一个日期排班 <!ELEMENT date (#PCDATA)> //data日期元素是PCDATA类型的 <!ELEMENT holiday (#PCDATA)> //holiday 假期也是PCDATA类型,说明没有具体节目内容 <!ELEMENT programslot (time,title,description?)> //programslot子元素又包括time,title,零次或一次description <!ELEMENT time (#PCDATA)> <!ELEMENT title (#PCDATA)> <!ELEMENT description (#PCDATA)> <!ATTLIST tvschedule name CDATA #REQUIRED> //tvschedule 必须有属性name <!ATTLIST channel chan CDATA #REQUIRED> //channel必须有属性chan <!ATTLIST programslot vtr CDATA #IMPLIED> <!ATTLIST title rating CDATA #IMPLIED> <!ATTLIST title language CDATA #IMPLIED> ]>
这里有个问题,<!ELEMENT day (date,(holiday|programslot+)+)>
这里的(holiday|programslot+)外边已经有“+”号了,里边programslot+还有意义么?
DTD实战就浅尝辄止到这里,明显感觉到一个问题是,元素的类型过少啊,只有PCDATE和CDATA两种,如果我是非文本类型呢?希望在SCHEMA里找到答案。