替换和插入文本
下面要做的事情就是自定义解析器,你可以知道如何得到通常会忽略的信息。但是在这之前,先要学习一些重要的XML概念。本节中,要学习:
· 处理特殊字符("<"
, "&"
,等等)
· 使用XML风格语法处理文本
处理特殊字符
XML中,实体是具有名字的XML结构(或纯文本)。通过名字引用实体将实体插入到文档中实体引用的位置。创建实体引用时,使用“&”和分号将实体名包围起来,如下所示:
&entityName;
后面学习如何编写DTD时,你会发现可以自己定义实体,所以&yourEntityName;
可以变成你定义的实体的所有文本。现在,主要讨论预定义实体和不需要任何特殊定义的字符引用。
预定义实体
类似于&
的实体引用包含一个名字,该名字在定界符之间 (本例中就是"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-风格的标记,这也会变成文档元素。
修改空元素
现在你已经理解了如何忽略空白的特定实例,可以修改“空”元素的定义了。现在可以扩展该元素以包含
<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中的实体
目前,已经看到了类似于&
这样的预定义实体,并且看到实体中可以引用属性。现在可以开始学习如何定义实体。
注意:这里定义的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
类。使用不同制造商的解析器,可以改变指向它的环境变量的值。可以在命令行中进行该操作,如下所示:
java -Djavax.xml.parsers.SAXParserFactory=yourFactoryHere
...
你指定的factory名必须是完全合格的类名(包括所有的包前缀)。如果想获得更多信息,请查看SAXParserFactory
类中的newInstance()
方法的文档。