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