1. XML語法
在說明DTD約束前,先介紹XML的基本語法:
文檔聲明
例:<?xml version="1.0" encoding="utf-8" standalone="yes"?>
元素定義:
由開始標記、屬性、元素內容、結束標記構成
例:<城市>清遠</城市>
屬性定義:
一個個元素中可以有多個屬性
例:<售價 單位="元">68</售價>
註釋:<!-- 這裏寫註釋內容 -->
DTD約束
DTD約束是早期出現的一種XML約束模式語言,根據它的語法創建出來的文件稱爲DTD文件。
DTD的引入方式:
外部式:
- <!DOCTYPE 根元素名稱 SYSTEM “外部DTD文件的URL”>
- <!DOCTYPE 根元素名稱 PUBLIC “DTD名稱” “外部DTD文件的URL”>
用一個例子來解釋如何使用外部式引入DTD
book.dtd文件
<!ELEMENT 書架 (書+)>
<!ELEMENT 書 (書名,作者,售價)>
<!ELEMENT 書名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售價 (#PCDATA)>
book.xml文件
<?xml version="1.0" encoding="utf-8"?>
<!-- 這裏引入book.dtd對book.xml進行約束 -->
<!DOCTYPE 書架 SYSTEM "book.dtd">
<書架>
<書>
<書名>***</書名>
<作者>***</作者>
<售價>10</售價>
</書>
<書>
<書名>****</書名>
<作者>****</作者>
<售價>100</售價>
</書>
</書架>
內嵌式:直接將DTD約束寫在XML文檔中
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE 書架 [
<!ELEMENT 書架 (書+)>
<!ELEMENT 書 (書名,作者,售價)>
<!ELEMENT 書名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售價 (#PCDATA)>
]>
<書架>
<書>
<書名>***</書名>
<作者>***</作者>
<售價>10</售價>
</書>
<書>
<書名>****</書名>
<作者>****</作者>
<售價>100</售價>
</書>
</書架>
DTD語法
DTD的結構一般由元素類型定義、屬性定義、實體定義、記號定義等構成
- 元素定義:每一條<!ELEMENT ...>語句定義一個元素
<!ELEMENT 元素名稱 元素內容>
在上面定義格式中,元素名稱是自定義的;元素內容是對元素包含內容的聲明,包括數據類型和符號兩部分,它共有5中形式:
- #PCDATA:表示元素中嵌套的內容是普通字符串,其中,關鍵字PCDATA是ParsedCharacter Data的簡寫。例如,<!ELEMENT 書名 (#PCDATA)> 表示書名所嵌套的內容是字符串類型。
- 子元素:說明元素包含的元素。例如:<!ELEMENT 書 (書名,作者,售價)>表示書中要嵌套書名、作者、售價等子元素
- 混合內容:表示元素既可以包含字符數據,也可以包含子元素。混合數據必須被定義零個或多個,例如:<!ELEMENT 書 (#PCDATA書名) *>表示書中嵌套的子元素書名包含零個或多個,並且書名是字符串文本格式。
- EMPTY:該元素是一個空元素。使用場景:元素在文檔中已經表明了明確含義,就可以在DTD中用關鍵字 EMPTY 表明空元素。例如:<!ELEMENT br EMPTY>,br是一個沒有內容的空元素。
- ANY:表示元素可以包含任何的字符數據和子元素。例如:<!ELEMENT 聯繫人 ANY>表示聯繫人可以包含任何形式的內容。但是在實際開發中,應該儘量避免使用ANY,因爲除了根元素外,其他使用ANY的元素都將失去DTD對XML文檔的約束效果。
在定義元素時,元素內容中可以包含一些符號,不同的符號具有不同的作用。具體如下:
- 問號[?]:表示該對象可以出現0次或1次
- 星號[*]:表示該對象可以出現0次或多次
- 加號[+]:表示該對象可以出現1次或多次
- 豎線[|]:表示在列出的對象中選擇1個
- 逗號[,]:表示對象必須按照指定的順序出現
- 括號[()]:用於給元素進行分組
- 屬性定義:基本語法格式如下
<!ATTLIST 元素名
屬性名1 屬性類型 設置說明
屬性名2 屬性類型 設置說明
......
>
在上面屬性定義的語法格式中,”元素名“是屬性所屬元素的名字,”屬性名“是屬性的名稱,”屬性類型“則用來指定該屬性屬於那種類型,”設置說明“用來說明該屬性是否必須出現。關於”屬性類型“和”設置說明“的相關講解如下:
2.1. 設置說明:定義元素的屬性時,有4中設置說明可以選擇
設置說明 | 含義 |
---|---|
#REQUIRED | 表示元素的該屬性是必須的,例如,當定義聯繫人信息的DTD時,我們希望每一個聯繫人都有一個聯繫電話屬性,這時,可以在屬性聲明時使用REQUIRED |
#IMPLED | 表示元素可以包含該屬性,也可以不包含該屬性。例如,當定義一本書的信息時,發現書的頁數屬性對讀者無關緊要,這時,在屬性聲明時可以使用IMPLIED |
#FIXED | 表示一個固定的屬性默認值,在XML文檔中不能將該屬性設置爲其他值。使用#FIXED關鍵字時,還需要爲該屬性提供一個默認值。當XML文檔中沒有定義該屬性時,其值將被自動設置爲DTD中定義的默認值 |
默認值 | 和FIXED一樣,如果元素不包含該屬性,該屬性將被自動設置爲DTD中定義的默認值。不同的是,該屬性的值是可以改變的,如果XML文件中設置了該屬性,新的屬性值會覆蓋DTD中定義的默認值 |
2.2. 屬性類型:在DTD中定義元素的屬性時,有10種屬性類型可以選擇,常見的幾種屬性類型如下
- CDATA
這是最常用的一種屬性類型,表明屬性類型是字符數據,與元素內容說明種的#PCDATA相同。當然,在屬性設置值中出現的特殊字符,也需要使用轉義字符序列來表示,例如,用”&“表示字符”&“,用”<“表示字符”<“等。 - Enumerated(枚舉類型)
在聲明屬性時,可以限制屬性的取值只能從一個列表中選擇,這類屬性屬於Enumerated(枚舉類型)。需要注意的是,在DTD定義中並不會出現關鍵字Enumerated。案例enum.xml展示如何定義Enumerated類型的屬性。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- 內嵌式DTD約束 -->
<!DOCTYPE 購物籃 [
<!ELEMENT 購物籃 ANY>
<!-- EMPTY表示肉及不包含字符數據,也不包含子元素,是一個空元素 -->
<!ELEMENT 肉 EMPTY>
<!ELEMENT 肉 品種 (雞肉|牛肉|豬肉|魚肉) "雞肉">
]>
<購物籃>
<肉 品種="魚肉"/>
<肉 品種="牛肉"/>
<肉/>
</購物籃>
在enum.xml中,“品種”屬性的類型是Enumerated,其值只能是“雞肉、牛肉、豬肉、魚肉”,而不能使用其他值。“品種”屬性的默認值是“雞肉”,所以,即使<購物籃>元素中的第三個子元素沒有顯示定義“品種”這個屬性,但它實際上也具有“品種”這個屬性,且屬性的取值爲“雞肉”。
- ID
一個ID類型的屬性用於唯一標識XML文檔中的一個元素。其屬性值必須遵守XML名稱定義的規則。一個元素只能有一個ID類型的屬性,而且ID類型的屬性必須設置爲#IMPLIED或#REQUIRED。因爲ID類型屬性的每一個取值都是用來標識一個特定元素,所以,爲ID類型的屬性提供默認值,特別是固定的默認值是毫無意義的。
<!-- 例:id.xml -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!ELEMENT 聯繫人列表 [
<!ELEMENT 聯繫人列表 ANY>
<!ELEMENT 聯繫人 (姓名,EMAIL)>
<!ELEMENT 姓名 (#PCDATA)>
<!ELEMENT EMAIL (#PCDATA)>
<!ELEMENT 聯繫人 編號 ID #REQUIRED>
]>
<聯繫人列表>
<聯繫人 編號="id1">
<姓名>張三</姓名>
<EMAIL>[email protected]</EMAIL>
</聯繫人>
<聯繫人 編號="id2">
<姓名>李四</姓名>
<EMAIL>[email protected]</EMAIL>
</聯繫人>
</聯繫人列表>
在 id.xml 中,將元素爲<聯繫人>的編號屬性設置爲#REQUIRED,說明每個聯繫人都有一個編號,同時,屬性編號的類型爲ID,說明編號是唯一的。這樣一來,通過編號就可以找到唯一對應的聯繫人了。
- IDREF和IDREFS
在上面的 id.xml 中,雖然張三和李四兩個聯繫人的ID編號是唯一的,但是這兩個ID類型的屬性沒有發揮作用,這時可以使用IDREF類型,使這兩個聯繫人之間建立一種一對一的關係。案例 Idref.xml 展示IDREF的使用:
<!-- 例:Idref.xml 演示如何使用IDREF -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE 聯繫人列表 [
<!ELEMENT 聯繫人列表 ANY>
<!ELEMENT 聯繫人 (姓名,EMAIL)>
<!ELEMENT 姓名 (#PCDATA)>
<!ELEMENT EMAIL (#PCDATA)>
<!ELEMENT 聯繫人
編號 ID #REQUIRED
上司 IDREF #IMPLIED
>
]>
<聯繫人列表>
<聯繫人 編號="id1">
<姓名>張三</姓名>
<EMAIL>[email protected]</EMAIL>
</聯繫人>
<聯繫人 編號="id2" 上司="id1">
<姓名>李四</姓名>
<EMAIL>[email protected]</EMAIL>
</聯繫人>
</聯繫人列表>
在 Idref.xml 中,爲元素<聯繫人列表>的子元素<聯繫人>增加一個名稱爲上司的屬性,並且將該屬性的類型設置爲IDREF,IDREF類型屬性的值必須爲一個已經存在的ID類型的屬性值。在第二個<聯繫人>元素中,將“上司”屬性設置爲第一個聯繫人的編號的屬性值,如此一來,就可以形成兩個聯繫人元素之間的對應關係,即李四的上司是張三。
IDREF可以是兩個元素之間形成一對一的關係,而IDREFS可以是元素之間形成一對多的關係。例如:學生借書(Library.xml)
<!-- Library.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library [
<!ELEMENT library (books,records)>
<!ELEMENT books (book+)>
<!ELEMENT book (title)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT records (item+)>
<!ELEMENT item (data,person)>
<!ELEMENT data (#PCDATA)>
<!ELEMENT person EMPTY>
<!ATTLIST book bookid ID #REQUIRED>
<!ATTLIST person name CDATA #REQUIRED>
<!ATTLIST person borrowed IDREFS #REQUIRED>
]>
<library>
<books>
<book bookid="b0101">
<title>xml基礎</title>
</book>
<book bookid="b0102">
<title>xml進階</title>
</book>
<book bookid="b0103">
<title>xml再進階</title>
</book>
</books>
<records>
<item>
<data>2019-12-26</data>
<person name="李四" borrowed="b0101 b0102"/>
</item>
<item>
<data>2019-12-27</data>
<person name="李四" borrowed="b0101 b0102 b0103"/>
</item>
</records>
</library>
除了上述的幾種屬性類型外,DTD約束中還有NMTOKEN、NMTOKENS、NOTITION、ENTITY和ENTITYS幾種屬性類型。