替換和插入文本
下面要做的事情就是自定義解析器,你可以知道如何得到通常會(huì)忽略的信息。但是在這之前,先要學(xué)習(xí)一些重要的XML概念。本節(jié)中,要學(xué)習(xí):
·?? ??處理特殊字符("<"
, "&"
,等等)
·?? 使用XML風(fēng)格語法處理文本
處理特殊字符
XML中,實(shí)體是具有名字的XML結(jié)構(gòu)(或純文本)。通過名字引用實(shí)體將實(shí)體插入到文檔中實(shí)體引用的位置。創(chuàng)建實(shí)體引用時(shí),使用“&”和分號(hào)將實(shí)體名包圍起來,如下所示:
??&entityName;
后面學(xué)習(xí)如何編寫DTD時(shí),你會(huì)發(fā)現(xiàn)可以自己定義實(shí)體,所以&yourEntityName;
可以變成你定義的實(shí)體的所有文本。現(xiàn)在,主要討論預(yù)定義實(shí)體和不需要任何特殊定義的字符引用。
預(yù)定義實(shí)體
類似于&
的實(shí)體引用包含一個(gè)名字,該名字在定界符之間 (本例中就是"amp")。它引用的文本(&)被用來替換名字,類似于C 或 C++程序中的宏。表 6-1 顯示了特殊字符的預(yù)定義實(shí)體。
表
6-1
預(yù)定義實(shí)體
|
字符
|
引用
|
&
|
&
|
<
|
<
|
>
|
>
|
"
|
"
|
'
|
'
|
字符引用
類似于“
的字符引用包含散列符號(hào)(#
),該符號(hào)后面跟著數(shù)字。數(shù)字是單個(gè)字符的Unicode值,如65代表字母"A",147代表左引號(hào),148表示右引號(hào)。在本例中,實(shí)體的 "name"為散列符號(hào),它后面跟著用來標(biāo)識(shí)字符的數(shù)字。
注意:XML中最好用十進(jìn)制來表示值。然而 http://www.unicode.org/charts/ 的Unicode表用十六進(jìn)制表示值。所以需要進(jìn)行轉(zhuǎn)換,以便將正確的值插入XML數(shù)據(jù)集中。
在XML文檔中使用實(shí)體引用
假設(shè)你想要在你的XML文檔中插入下面一行內(nèi)容:
?Market Size < predicted
直接在XML文件中添加該行的問題是,當(dāng)解析器看到左尖括號(hào)(<)時(shí),它開始查找標(biāo)簽名,這就會(huì)脫離解析。為了解決該問題,在文件中用<
替代"<"
。
注意:下面的修改結(jié)果包含在 slideSample03.xml
中。它的處理結(jié)果在Echo07-03.txt
.中。(可瀏覽版本是slideSample03-xml.html
和 Echo07-03.html
.)
如果你按照該編程教程進(jìn)行學(xué)習(xí),在r slideSample.xml
文件中添加下面的文本:
??<!-- OVERVIEW -->
??<slide type="all">
????<title>Overview</title>
????...
??</slide>
??<slide type="exec">
????<title>Financial Forecast</title>
????<item>Market Size < predicted</item>
????<item>Anticipated Penetration</item>
????<item>Expected Revenues</item>
????<item>Profit Margin </item>
??</slide>
</slideshow>
在XML文件上運(yùn)行該Echo程序時(shí),可以得到下列輸出結(jié)果:
ELEMENT:????????<item>
CHARS:????????Market Size < predicted
END_ELM:????????</item>
解析器將引用轉(zhuǎn)化為它表示的實(shí)體,并將該實(shí)體傳遞給應(yīng)用程序。
使用XML-風(fēng)格的語法處理文本
在處理包含多個(gè)特殊字符的XML或 HTML塊時(shí),使用合適的實(shí)體引用替換每個(gè)特殊字符很不方便。在這種情況下,可以使用CDATA
段。
注意:修改結(jié)果包含在slideSample04.xml
中。處理結(jié)果在Echo07-04.txt
中。(可瀏覽版本是 slideSample04-xml.html
和Echo07-04.html
.)
HTML中
CDATA
段的功能類似于<pre>...</pre>
,只是—— CDATA
段中的所有空白都很重要,并且不將其中的字符解釋為XML。CDATA
段從<![CDATA[
開始
,并以]]>
結(jié)束。將下面的文本添加到slideSample.xml
文件,為虛構(gòu)的技術(shù)幻燈片定義CDATA
段:
?? ...
??<slide type="tech">
????<title>How it Works</title>
????<item>First we fozzle the frobmorten</item>
????<item>Then we framboze the staten</item>
????<item>Finally, we frenzle the fuznaten</item>
????<item><![CDATA[Diagram:
??????frobmorten <--------------- fuznaten
????????| ?? <3>??^
????????| <1>????|??<1> = fozzle
????????V ????|??<2> = framboze
????????Staten--------------------+??????<3> = frenzle
?????????? <2>
????]]></item>
??</slide>
</slideshow>
在新文件上運(yùn)行Echo 程序時(shí),得到下列結(jié)果:
??ELEMENT: <item>
??CHARS:?? Diagram:
?
frobmorten <--------------fuznaten
?????| ?????????<3>??????????^
???? | <1>? ????????????????|?? <1> = fozzle
????V? ????????????????|?? <2> = framboze
??staten----------------------+?? <3> = frenzle
???????????<2>
?
END_ELM: </item>
可以看到一旦編寫了CDATA
段中的文本,就能夠得到它。由于解析器不將尖括號(hào)作為XML,所以它們不會(huì)產(chǎn)生致命錯(cuò)誤 (因?yàn)椋绻饫ㄌ?hào)不在CDATA段中,文檔的結(jié)構(gòu)就不好)。
處理CDATA 和其他字符
CDATA的存在使得正確回送XML非常重要。如果要輸出的文本不在CDATA段中,那么必須使用適當(dāng)?shù)膶?shí)體引用替代文本中的尖括號(hào)、&和其他特殊符號(hào)。(替代左尖括號(hào)和&最重要, 如果不誤導(dǎo)解析器,它就能正確解釋其他字符)
但是如果輸出結(jié)果在CDATA 段中,那么不用替換,以產(chǎn)生類似于上例中的文本。在類似于Echo這樣的簡單程序中,這并不是一件重要的事情。但是許多XML-過濾應(yīng)用程序希望進(jìn)行跟蹤,確定文本是否出現(xiàn)在CDATA段中,這樣是為了正確處理特殊字符。
另一處需要留意的地方就是屬性。屬性值的文本中也能包含尖括號(hào)和分號(hào),也需要使用實(shí)體引用來替換它。(屬性文本不能在CDATA段中,所以,這里不會(huì)存在替換問題)
本教程的后面部分,會(huì)介紹如何使用LexicalHandler
來判斷是否正在處理CDATA 段。然后,會(huì)介紹如何定義DTD。
創(chuàng)建文檔類型定義 (DTD)
在XML聲明之后,文檔序言中能引入DTD,通過DTD可以指定XML文檔中可以引入的標(biāo)簽。同時(shí)告訴驗(yàn)證解析器哪些標(biāo)簽是有效的,以及在什么布局下是有效的。DTD告訴驗(yàn)證解析器和非驗(yàn)證解析器希望在哪里出現(xiàn)文本,解析器根據(jù)什么原則判斷空白是不可忽略的還是可以忽略的。
基本DTD定義
例如,在解析幻燈片放映時(shí),你會(huì)看到在注釋和幻燈片元素之前和之后多次調(diào)用方法characters
。在這些情況下,空白由行結(jié)束符和標(biāo)簽周圍的縮進(jìn)構(gòu)成。目標(biāo)是使得XML文檔可讀——空白并不總是文檔內(nèi)容的一部分。在開始學(xué)習(xí)DTD定義之前,首先告訴解析器什么地方的空白可以忽略。
注意:本節(jié)定義的DTD包含在 slideshow1a.dtd
中。 (可瀏覽的版本是 slideshow1a-dtd.html
.)
創(chuàng)建文件slideshow.dtd
。 輸入XML聲明和注釋標(biāo)識(shí)該文件,如下所示:
<?xml version='1.0' encoding='utf-8'?>
<!--
??DTD for a simple "slide show".
-->
然后,添加下面的文本,指定slideshow
元素僅包含slide
元素:
<!-- DTD for a simple "slide show". -->
<!ELEMENT slideshow (slide+)>
可以看到,DTD標(biāo)簽以<!
開始,接著是標(biāo)簽名 (ELEMENT
)。標(biāo)簽名之后是要定義的元素的名字 (slideshow
),括號(hào)內(nèi)是一個(gè)或多個(gè)項(xiàng)目,表明元素的有效內(nèi)容。這樣,符號(hào)說明slideshow
包含一個(gè)或多個(gè)slide
元素。
若沒有+號(hào),該定義則表明slideshow
僅包含單個(gè)slide
元素。表6-2列出了在元素定義中可以使用的符號(hào)。
表
6-2 ?DTD
元素限定符
|
限定符
|
名字
|
含義
|
?
|
問號(hào)
|
可選 (零個(gè)或一個(gè))
|
*
|
星號(hào)
|
零個(gè)或多個(gè)
|
+
|
加號(hào)
|
一或多個(gè)
|
括號(hào)內(nèi)可以引入多個(gè)元素,使用逗號(hào)分隔,并且可以在每個(gè)元素上使用一個(gè)符號(hào)來表明可能會(huì)出現(xiàn)該元素的多少個(gè)實(shí)例。逗號(hào)分隔列表說明了哪些元素是有效的并說明了它們出現(xiàn)的順序。
也可以嵌套括號(hào)以分組多個(gè)項(xiàng)目。例如,在定義image
元素后,可以通過((image, title)+)
聲明在幻燈片中每個(gè)image
元素必須和title
元素配對使用。這里,在image/title
對上使用加號(hào)表明可以出現(xiàn)一個(gè)或多個(gè)(image
,
title
)對。
定義文本和嵌套元素
現(xiàn)在,已經(jīng)告訴了解析器哪里不能出現(xiàn)文本,下面來看看如何告訴解析器哪里能夠出現(xiàn)文本。添加下面的文本,以定義slide
、 title
、item
和list
元素:
<!ELEMENT slideshow (slide+)>
<!ELEMENT slide (title, item*)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT item (#PCDATA | item)* >
添加的第一行說明幻燈片包含一個(gè)title
,它后面是零個(gè)或多個(gè)元素項(xiàng)。這里沒有新的內(nèi)容了。下一行說明標(biāo)題包含完整的解析字符數(shù)據(jù) (PCDATA
)。在該領(lǐng)域中這叫做"文本",但是在XML中,這叫做“解析字符數(shù)據(jù)”。 (這就將它和CDATA
段區(qū)分開來了,CDATA可能包含不需要解析的字符數(shù)據(jù)。) PCDATA后面的"#"
號(hào)表明下面跟著的是特殊字,而不是元素名。
最后一行介紹了垂直條(|
),它代表了or 條件。這樣,可能出現(xiàn)PCDATA
或item
。結(jié)尾處的星號(hào)表明它們中的任何一個(gè)可以連續(xù)出現(xiàn)零次或多次。該規(guī)范是混合內(nèi)容模型,因?yàn)槿我舛鄠€(gè)item
元素可以包含在文本中。定義這樣的模型時(shí)要先指定#PCDATA
,并使用垂直條分割一些可選項(xiàng),最后以星號(hào)結(jié)束。
DTD的局限性
如果能夠指定item
可以包含文本或在項(xiàng)目列表后可以有文本就更好了。但是事實(shí)表明在DTD中很難得到這樣的規(guī)范。例如,可以使用下面的方法定義一個(gè)item
:
<!ELEMENT item (#PCDATA | (#PCDATA, item+)) >
這樣當(dāng)然很精確,但是一旦解析器看到了#PCDATA 和垂直條,它就要求剩下的定義遵守混合內(nèi)容模型。但是該規(guī)范沒有遵守該模型,所以得到錯(cuò)誤:Illegal mixed content model for 'item'. Found ( ...,
(
'item'
使用了非法的混合內(nèi)容模型。找到
( ...,
)
這里的十六進(jìn)制字符28表示結(jié)束該定義的尖括號(hào)。
同樣,重復(fù)定義item 元素是沒有用的。運(yùn)行驗(yàn)證解析器的時(shí)候,下面的規(guī)范:
<!ELEMENT item (#PCDATA) >
<!ELEMENT item (#PCDATA, item+) >
會(huì)產(chǎn)生"duplicate definition"(“重復(fù)定義”)警告。實(shí)際上,第二個(gè)定義會(huì)被忽略。所以現(xiàn)在看來定義一個(gè)混合內(nèi)容模型 (它允許item
元素包含在文本中)是目前能用的最好方法。
除了上面提到的混合內(nèi)容模型的局限性外,沒有其他方法來限制指定的PCDATA
可能出現(xiàn)的文本。它是不是僅能包含數(shù)字?它是不是必須是日期格式的,還是能夠是貨幣格式?在DTD上下文中沒有辦法說明。
最后,注意DTD不是層次結(jié)構(gòu)的。title
元素定義的應(yīng)用等價(jià)于slide
標(biāo)題和item
標(biāo)題。當(dāng)擴(kuò)展DTD允許使用HTML-風(fēng)格的標(biāo)簽和純文本時(shí),就有必要根據(jù)slide標(biāo)題限制item標(biāo)題的大小。但是這樣做的唯一方法是給它們一個(gè)不同的名字,如“item-title
”
。 由于DTD缺少層次結(jié)構(gòu),最后一行要求在命名空間中引入“字符連接層次結(jié)構(gòu)”(或它的等價(jià)物) 。所有這些局限性都是開發(fā)模式規(guī)范標(biāo)準(zhǔn)的基本動(dòng)機(jī)。
DTD中的特殊元素值
最好不要使用括號(hào)擴(kuò)起來的元素列表,元素定義可以使用一或兩個(gè)特殊值: ANY
或 EMPTY
。ANY
規(guī)范表示元素可能包含其他定義的元素或PCDATA
。這類規(guī)范通常用在通用的XML文檔(如創(chuàng)建字處理器)根元素中。在這類文檔中原文的元素可以以任何順序出現(xiàn),所以使用ANY
是有意義的。
EMPTY
規(guī)范表示元素不包含任何內(nèi)容。所以允許你使用<flag/>
標(biāo)簽的
e-mail消息在DTD中有一行跟下面類似的內(nèi)容:
<!ELEMENT flag EMPTY>
引用DTD
這時(shí),DTD定義跟XML文檔不在同一文檔中。這意味著必須在XML 文檔中引用它,這使得DTD文件成為XML文件完整的文檔類型定義(DTD)的外部子集。后面可以看到,也可以在文檔中引入DTD的一部分。這樣的定義構(gòu)成了DTD的本地子集。
注意:本節(jié)中編寫的XML包括在slideSample05.xml
中。(可瀏覽版本是slideSample05-xml.html
)。
將下面幾行內(nèi)容添加到slideSample.xml
文件中引用DTD文件:
<!--? A SAMPLE set of slides? -->
<!DOCTYPE slideshow SYSTEM "slideshow.dtd">
<slideshow ?
同樣,DTD 標(biāo)簽以"<!"
開始,在本例中,標(biāo)簽名DOCTYPE
說明該文檔是slideshow
,意味著文檔包含slideshow
元素和它內(nèi)部的所有內(nèi)容:
<slideshow>
...
</slideshow>
該標(biāo)簽將slideshow
元素定義成文檔的根元素。每個(gè)XML文檔必須有一個(gè)根元素。就在這里指定該元素。換句話說,該標(biāo)簽將文檔content 認(rèn)為是slideshow
。
DOCTYPE
標(biāo)簽出現(xiàn)在XML聲明之后根元素之前。SYSTEM
標(biāo)識(shí)符指定DTD文件的位置。由于它不以http:/
or file:/
這類前綴開始,故路徑跟XML文檔的位置相關(guān)。是否還記得setDocumentLocator
方法?解析器使用該信息查找DTD 文件,這就跟應(yīng)用程序查找和XML文檔相關(guān)的文件一樣。也要使用PUBLIC
標(biāo)識(shí)符指定使用唯一名字的DTD 文件——但是解析器必須能夠處理它。
DOCTYPE
規(guī)范必須在XML文檔內(nèi)包含DTD 定義。這樣的定義包含在方括號(hào)內(nèi),如下所示:
<!DOCTYPE slideshow SYSTEM "slideshow1.dtd" [
??...local subset definitions here...
]>
后面會(huì)充分利用該功能來定義一些文檔中能使用的實(shí)體。
DTD對非驗(yàn)證解析器的影響
在前一節(jié),定義了基本文檔類型,并在XML文件中使用了它。在本節(jié)中,將使用Echo程序說明引入DTD后,數(shù)據(jù)是如何出現(xiàn)在SAX解析器中的。
注意:本節(jié)中的輸出結(jié)果在Echo07-05.txt
中。(可瀏覽版本是Echo07-05.html
)
在slideSample.xml
的最新版本上運(yùn)行Echo程序,可以看到許多對characters
方法的多余的調(diào)用消失了。
以前是:
??...
>
PROCESS: ...
CHARS:
??ELEMENT:????????<slide
????ATTR: ...
??>
??????ELEMENT:????????<title>
??????CHARS:????????Wake up to ...
??????END_ELM:????????</title>
??END_ELM:????????</slide>
CHARS:
??ELEMENT:????????<slide
????ATTR: ...
??>
??...
現(xiàn)在是:
??...
>
PROCESS: ...
??ELEMENT:????????<slide
????ATTR: ...
??>
??????ELEMENT:????????<title>
??????CHARS:????????Wake up to ...
??????END_ELM:????????</title>
??END_ELM:????????</slide>
??ELEMENT:????????<slide
????ATTR: ...
??>
??...
很明顯,可以看到,解析器不再傳送以前回送到slide
元素旁邊的空白字符,這是因?yàn)镈TD聲明, slideshow
僅僅包含slide
元素:
? <!ELEMENT slideshow (slide+)>
跟蹤可忽略空白
既然使用了DTD,解析器不再調(diào)用帶有無關(guān)空白的characters
方法。從僅對處理XML數(shù)據(jù)感興趣的應(yīng)用程序的觀點(diǎn)來看,這樣做非常好。應(yīng)用程序不再受到那些僅為增加XML文件可讀性的空白的干擾。
另外,如果你正在編寫過濾XML數(shù)據(jù)文件的應(yīng)用程序,并且你希望該版本的文件具有相同的可讀性,那么這些空白就不會(huì)是無關(guān)的——它是必需的。要得到這些字符,必須在應(yīng)用程序中添加方法ignorableWhitespace
。下面你就要這樣做。
注意:本節(jié)中編寫的代碼包含在Echo08.java
中。輸出結(jié)果在Echo08-05.txt
.中。(可瀏覽版本是Echo08-05.html
)
要處理解析器看到的可忽略的空白,添加下面的代碼,實(shí)現(xiàn)你自己的Echo程序中的ignorableWhitespace
事件處理程序:
public void characters (char buf[], int offset, int len)
...
}
public void ignorableWhitespace char buf[], int offset, int Len)
throws SAXException
{
??nl();
??emit("IGNORABLE");
}
public void processingInstruction(String target, String data)
...
該代碼僅僅產(chǎn)生告訴你查看到了可忽略空白的消息。
注意:同樣,不是創(chuàng)建的所有解析器都是相同的。SAX規(guī)范不要求調(diào)用該方法。只要DTD使得它可能,Java XML實(shí)現(xiàn)就這樣做。
現(xiàn)在運(yùn)行Echo應(yīng)用程序,輸出結(jié)果如下所示:
ELEMENT: <slideshow
??ATTR: ...
>
IGNORABLE
IGNORABLE
PROCESS: ...
IGNORABLE
IGNORABLE
??ELEMENT: <slide
????ATTR: ...
??>
??IGNORABLE
????ELEMENT: <title>
????CHARS:?? Wake up to ...
????END_ELM: </title>
??IGNORABLE
??END_ELM: </slide>
IGNORABLE
IGNORABLE
??ELEMENT: <slide
????ATTR: ...
??>
??...
這里很明顯,在注釋和幻燈片元素之前和之后調(diào)用了可忽略空白,其中這之前調(diào)用的字符是DTD。
清除
既然已經(jīng)回送了可忽略空白,從你的Echo程序中刪除該代碼——前面的練習(xí)中已經(jīng)不再需要它們了。
注意:該變化保存在Echo09.java
中。
文檔和數(shù)據(jù)
前面,已經(jīng)介紹了同時(shí)存在XML documents和XML data這兩種叫法的原因之一在于,XML不管結(jié)構(gòu)中的元素之間是否允許使用文本,都能能否很好地處理它們。
在使用的示例文件中,slideshow
元素是數(shù)據(jù)元素(data element)的一個(gè)例子——它僅包含子元素,且沒有插入文本。另外,item
元素可能會(huì)變成文檔元素(document element),因?yàn)樗芤肓宋谋竞妥釉亍?
在閱讀該教程的過程中,你會(huì)知道如何擴(kuò)展標(biāo)題元素的定義,讓它能引入HTML-風(fēng)格的標(biāo)記,這也會(huì)變成文檔元素。
修改空元素
現(xiàn)在你已經(jīng)理解了如何忽略空白的特定實(shí)例,可以修改“空”元素的定義了。現(xiàn)在可以擴(kuò)展該元素以包含
? <foo>?? </foo>
其中標(biāo)簽間有空白并且DTD定義的空白可忽略。
定義DTD中的屬性和實(shí)體
目前定義的DTD用于非驗(yàn)證解析器。它說明了文本應(yīng)該出現(xiàn)在哪里不應(yīng)該出現(xiàn)在哪里,這些都是解析器所關(guān)心的。但是在驗(yàn)證解析器的使用中,DTD需要指定不同元素的有效屬性。我們將在本節(jié)中實(shí)現(xiàn)它,然后再定義一個(gè)可以在XML文件中引用的內(nèi)部實(shí)體和外部實(shí)體。
定義DTD中的屬性
首先在幻燈片放映中定義元素屬性。
注意:本節(jié)中編寫的XML包含在slideshow1b.dtd
中。 (可瀏覽版本是:slideshow1b-dtd.html
)
添加下面的文本,定義slideshow
元素的屬性:
<!ELEMENT slideshow (slide+)>
<!ATTLIST slideshow
????title??? CDATA??? #REQUIRED
????date???? CDATA??? #IMPLIED
????author?? CDATA??? "unknown"
>
<!ELEMENT slide (title, item*)>
DTD標(biāo)簽ATTLIST
揭開了屬性定義的序幕。ATTLIST
后面的名字指定了要定義屬性的元素。這里,元素是slideshow
元素。(注意DTD規(guī)范中同樣缺少層次結(jié)構(gòu)) 。
每個(gè)屬性由被三個(gè)空格隔開的值指定。不允許使用逗號(hào)和其他分隔符,所以如上所述的格式化定義對增強(qiáng)可讀性很有效。每行中的第一個(gè)元素是屬性的名字,這里是:title
, date
或 author
。 第二個(gè)元素表示了數(shù)據(jù)的類型:CDATA
是字符數(shù)據(jù)——不可解析數(shù)據(jù)。左尖括號(hào)(<)同樣是XML標(biāo)簽的一部分。表 6-3 提供了屬性類型的有效選擇。
表
6-3
屬性類型
|
屬性類型
|
指定了
...
|
(value1 | value2 | ...)
|
由垂直條分隔的值的列表 (例子在下面)
|
CDATA
|
"未解析的字符數(shù)據(jù)"。(對于一般人而言,這是文本字符串)
|
ID
|
不與其他ID屬性共享的名字
|
IDREF
|
對文檔其他地方定義的ID的引用
|
IDREFS
|
包含一個(gè)或多個(gè)ID引用的空格分隔的列表
|
ENTITY
|
DTD中定義的實(shí)體的名字
|
ENTITIES
|
空格間隔的實(shí)體的列表
|
NMTOKEN
|
有效的XML名字,由字母、數(shù)字、連字符、下劃線和冒號(hào)構(gòu)成
|
NMTOKENS
|
用空格間隔的名字列表
|
NOTATION
|
DTD指定的符號(hào)名,描述了非XML數(shù)據(jù)格式,比如圖像文件中使用的*
|
*這是一個(gè)很快就過時(shí)的規(guī)范,在本節(jié)結(jié)尾處會(huì)詳細(xì)介紹。
當(dāng)屬性類型包含括號(hào)括起來的由垂直條分隔的選項(xiàng)列表時(shí),屬性必須使用指定值。例如,將下面的文本添加到DTD中:
<!ELEMENT slide (title, item*)>
<!ATTLIST slide
????type?? (tech | exec | all) #IMPLIED
>
<!ELEMENT title (#PCDATA)>
<!ELEMENT item (#PCDATA | item)* >
該規(guī)范說明slide
元素的type
屬性必須按如下方式給出:type="tech"
type="exec"
, or type="all"
。其他值不能接受。(能理解DTD的XML編輯器可以使用這樣的規(guī)范來表現(xiàn)選項(xiàng)的彈出列表。)
屬性規(guī)范中的最后一項(xiàng)確定了屬性的默認(rèn)值,并說明了是否需要屬性。表6-4 列出了可用選項(xiàng)。
表
6-4
屬性
-
規(guī)范參數(shù)
|
規(guī)范
|
指定了
...
|
#REQUIRED
|
文檔中必須指定屬性值
|
#IMPLIED
|
文檔中不需要指定屬性值。如果沒有指定,它會(huì)使用應(yīng)用程序提供的默認(rèn)值
|
"defaultValue"
|
如果文檔中沒有指定值,這就是要使用的默認(rèn)值
|
#FIXED
"fixedValue"
|
要使用的值。如果文檔指定了所有值,它們必須相同
|
定義DTD中的實(shí)體
目前,已經(jīng)看到了類似于&
這樣的預(yù)定義實(shí)體,并且看到實(shí)體中可以引用屬性。現(xiàn)在可以開始學(xué)習(xí)如何定義實(shí)體。
注意:這里定義的XML包含在slideSample06.xml
中。輸出結(jié)果在Echo09-06.txt
中。 (可瀏覽版本是slideSample06-xml.html
和Echo09-06.html
.)
將下面的文本添加到你的XML文件的DOCTYPE
標(biāo)簽中:
<!DOCTYPE slideshow SYSTEM "slideshow.dtd" [
??<!ENTITY product? "WonderWidget">
??<!ENTITY products "WonderWidgets">
]
>
ENTITY
標(biāo)簽名說明正在定義一個(gè)實(shí)體。下面是實(shí)體名和它的定義。這里,定義了一個(gè)叫做“product”的實(shí)體,使用它來替代產(chǎn)品名。以后,一旦改變了產(chǎn)品名(通常會(huì)這樣),只需要改變一個(gè)地方的名字,然后,所有的幻燈片就都能夠使用該新值。
最后一部分是XML文檔中實(shí)體引用時(shí)可以取代實(shí)體名的替換字符串。使用引號(hào)定義替代字符串,當(dāng)將文本插入文檔時(shí)沒有引入它們。
為方便起見,我們定義了兩個(gè)版本,一個(gè)是單數(shù)一個(gè)是復(fù)數(shù)。當(dāng)市場專家遇到產(chǎn)品名"Wally"時(shí),你將要輸入復(fù)數(shù)"Wallies"并且正確替換它。
注意:老實(shí)說,這屬于外部DTD的范疇。這樣,當(dāng)名字改變時(shí),所有的文檔能夠引用新的名字。但是,這是一個(gè)例子...
既然已經(jīng)定義了實(shí)體,下面要在幻燈片放映中引用它們。進(jìn)行下面的修改實(shí)現(xiàn)該功能:
<slideshow
??title="WonderWidget&product;
Slide Show"
??...
??<!-- TITLE SLIDE -->
??<slide type="all">
????<title>Wake up to WonderWidgets&products;
!</title>
??</slide>
?? <!-- OVERVIEW -->
??<slide type="all">
????<title>Overview</title>
????<item>Why <em>WonderWidgets&products;
</em> are
great</item>
????<item/>
????<item>Who <em>buys</em> WonderWidgets&products;
</item>
??</slide>
這里要注意的是,你定義的實(shí)體被引用時(shí)用到的語法和用于預(yù)定義實(shí)體的相同(&entityName;
),并且可以在屬性值和元素的內(nèi)容中引用實(shí)體。
回送實(shí)體引用
下面是在該版本的文件上運(yùn)行Echo 程序時(shí)看到的內(nèi)容:
ELEMENT:????????<title>
CHARS:????????Wake up to WonderWidgets
!
END_ELM:????????</title>
注意,已經(jīng)使用了產(chǎn)品名替換了實(shí)體引用。
其他有用實(shí)體
下面是編寫XML文檔中可能有用的其他幾個(gè)實(shí)體定義的例子:
<!ENTITY ldquo? "“"> <!-- Left Double Quote -->
<!ENTITY rdquo? "”"> <!-- Right Double Quote -->
<!ENTITY trade? "™"> <!-- Trademark Symbol (TM) -->
<!ENTITY rtrade "®"> <!-- Registered Trademark (R) -->
<!ENTITY copyr? "©"> <!-- Copyright Symbol --> ?
引用外部實(shí)體
也可以使用SYSTEM
或 PUBLIC
標(biāo)識(shí)符命名在外部文件中定義的實(shí)體。現(xiàn)在就可以這樣做。
注意:這里定義的XML包含在 slideSample07.xml
和 copyright.xml
中。輸出結(jié)果在Echo09-07.txt
中。(可瀏覽版本是slideSample07-xml.html
, copyright-xml.html
和 Echo09-07.html
.)
為了引用外部實(shí)體,將下面的文本添加到XML文件的DOCTYPE
語句中:
<!DOCTYPE slideshow SYSTEM "slideshow.dtd" [
??<!ENTITY product? "WonderWidget">
??<!ENTITY products "WonderWidgets">
??<!ENTITY copyright SYSTEM "copyright.xml">
]>
該定義引用了一個(gè)包含在文件copyright.xml
中的版本信息。創(chuàng)建該文件并輸入一些有趣的文本,如下所示:
? <!--? A SAMPLE copyright? -->
This is the standard copyright message that our lawyers
make us put everywhere so we don't have to shell out a
million bucks every time someone spills hot coffee in their
lap...
最后,將下面的文本添加到slideSample.xml
文件中,以引用外部實(shí)體:
<!-- TITLE SLIDE -->
??...
</slide>
<!-- COPYRIGHT SLIDE -->
<slide type="all">
??<item>©right;</item>
</slide>
也可以使用外部實(shí)體聲明來訪問servlet,該servlet使用類似于下面的定義來產(chǎn)生當(dāng)前日期:
<!ENTITY currentDate SYSTEM
??"http://www.example.com/servlet/CurrentDate?fmt=dd-MMM-
yyyy"> ?
可以使用跟引用其他實(shí)體相同的方法引用該實(shí)體:
? Today's date is ¤tDate;.
回送外部實(shí)體
下面是在幻燈片放映的最新版本上運(yùn)行Echo程序時(shí)看到的內(nèi)容:
...
END_ELM: </slide>
ELEMENT: <slide
??ATTR: type????????"all"
>
??ELEMENT: <item>
??CHARS:
This is the standard copyright message that our lawyers
make us put everywhere so we don't have to shell out a
million bucks every time someone spills hot coffee in their
lap...
??END_ELM: </item>
END_ELM: </slide>
...
注意,文件中注釋后面的新行將作為字符回送,但是忽略注釋本身。這就是為什么版本消息出現(xiàn)在CHARS:
標(biāo)簽后面的下一行中,而不是直接在標(biāo)簽后面——實(shí)際上第一個(gè)回送的字符是注釋后面的新行。
總結(jié)實(shí)體
文檔內(nèi)容中引用的實(shí)體,不論是外部的還是內(nèi)部的都一概叫做一般實(shí)體。包含DTD內(nèi)引用的DTD規(guī)范的實(shí)體叫做參數(shù)實(shí)體(后面將會(huì)詳細(xì)介紹)。
一個(gè)包含XML(文本和標(biāo)記)并且被解析的實(shí)體叫做已析實(shí)體(parsed entity)。一個(gè)包含二進(jìn)制數(shù)據(jù)的實(shí)體 (如圖像)叫做未析實(shí)體(unparsed entity)。(本質(zhì)來講,它必須是外部的)該教程的下一節(jié)主要討論對未析實(shí)體的引用。
引用二進(jìn)制實(shí)體
本節(jié)沒有編程練習(xí)。主要討論引用類似于圖像文件和多媒體數(shù)據(jù)文件這樣的二進(jìn)制時(shí)的選項(xiàng)。
使用MIME數(shù)據(jù)類型
有兩種方法可以引用類似于二進(jìn)制圖形文件的這類未析實(shí)體。一種方法是使用DTD的NOTATION
-規(guī)范機(jī)制。然而,該機(jī)制是一個(gè)復(fù)雜的非直觀機(jī)制,通常用于兼容SGML文檔。在介紹DTDHandler
API時(shí)會(huì)進(jìn)一步詳細(xì)地介紹它,但是現(xiàn)在已可以說結(jié)合最近定義的XML命名空間標(biāo)準(zhǔn),以及為電子郵件附件定義的MIME數(shù)據(jù)類型,為引用未析外部實(shí)體提供了一個(gè)更加有用、更易理解、更易擴(kuò)展的機(jī)制。
注意:這里描述的XML在 slideshow1b.dtd
中。我們實(shí)際上沒有回送任何圖像。這超過了本教程的Echo程序的范圍。本節(jié)僅用來理解如何進(jìn)行這類引用。它假設(shè)將要處理XML數(shù)據(jù)的應(yīng)用程序知道如何處理這類引用。
將下面的文本添加到slideshow.dtd
文件中
,以便讓幻燈片顯示使用圖像文件:
<!ELEMENT slide (image?, title, item*)>
<!ATTLIST slide
????type?? (tech | exec | all) #IMPLIED
>
<!ELEMENT title (#PCDATA)>
<!ELEMENT item (#PCDATA | item)* >
<!ELEMENT image EMPTY>
<!ATTLIST image
????alt??? CDATA??? #IMPLIED
????src??? CDATA??? #REQUIRED
????type?? CDATA??? "image/gif"
>
這些改動(dòng)聲明image
是slide
中的可選元素,將它定義為空元素,并定義它需要的屬性。該image
標(biāo)簽在HTML 4.0 標(biāo)簽img
之后,添加了圖像類型標(biāo)識(shí)符type
。(img
標(biāo)簽是在HTML 4.0規(guī)范中定義的)
image
標(biāo)簽的屬性是由ATTLIST
項(xiàng)定義的。alt
屬性定義了在找不到圖像的情況下顯示的替代文本,它接受字符數(shù)據(jù)(CDATA
)。這是一個(gè)“隱含”(implied)值,這表明它不是可選的,并且處理數(shù)據(jù)的程序知道如何替代像“圖像沒有找到”這樣的消息。另外,需要src
屬性來指定要顯示的圖像。
在MIME數(shù)據(jù)類型中需要type
屬性,它定義在ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/
.。它的默認(rèn)值為 image/gif
。
注意:這里可以知道類型屬性使用的字符數(shù)據(jù)(CDATA
)是MIME數(shù)據(jù)類型中的一種。最常見的格式為: image/gif
和image/jpeg
。這樣,最好在這里指定屬性列表,使用下面的格式:
type ("image/gif", "image/jpeg")
然而,這沒有用,因?yàn)閷傩粤斜硎芟抻诿钟浱?hào)(name token)。正斜杠不是名字記號(hào)字符的有效部分,所以該聲明出錯(cuò)。而且,在DTD中創(chuàng)建屬性列表將會(huì)將有效的MIME類型限制于這些定義中。仍然保持CDATA
使得它更加開放,所以定義了其他類型之后,該聲明仍然有效。
文檔中,對"intro-pic"圖像的引用具有以下格式:
<image src="image/intro-pic.gif", alt="Intro Pic",
type="image/gif" />
另一選擇:使用實(shí)體引用
使用MIME數(shù)據(jù)類型作為元素的屬性是一個(gè)非常靈活并可擴(kuò)展的機(jī)制。要使用符號(hào)機(jī)制創(chuàng)建外部ENTITY
引用,對于jpeg和gif數(shù)據(jù)需要DTD NOTATION
元素。當(dāng)然這些都可以從一些中央知識(shí)庫中取得。但是這樣你必須要為你想要引用的每個(gè)圖像定義不同的ENTITY
元素。換句話說,在文檔中添加新的圖像需要在DTD中進(jìn)行新實(shí)體定義也要在文檔中引用它。給定了普遍存在的HTML 4.0規(guī)范,更新的標(biāo)準(zhǔn)要使用MIME數(shù)據(jù)類型和聲明,如image
,它假設(shè)應(yīng)用程序知道如何處理這類元素。
選擇解析器實(shí)現(xiàn)
如果沒有指定其他factory類,就使用默認(rèn)的SAXParserFactory
類。使用不同制造商的解析器,可以改變指向它的環(huán)境變量的值。可以在命令行中進(jìn)行該操作,如下所示:
java -Djavax.xml.parsers.SAXParserFactory=yourFactoryHere
...
你指定的factory名必須是完全合格的類名(包括所有的包前綴)。如果想獲得更多信息,請查看SAXParserFactory
類中的newInstance()
方法的文檔。