XML教程

6章 完整的XML文档

 

HTML

      4.0有大约100个不同的标记,大部分标记都有多个可能的属性用于几百种不同的变化。因为XML的功能比HTML强大,你也许认为需要懂得更多标记,但不是这样。XML凭借其简洁性和扩展性具有强大的功能,并不是大量的标记。

 

      事实上,XML几乎没有预先定义任何标记,相反允许用户在需要时定义自己的标记。但是由自定义标记建立的这些标记和文档并不是随意的,必须遵循一组特定的规则,本章将详细阐述这些规则。遵守这些规则的文档被认为是结构完整的。结构完整是XML处理器和浏览器阅读文件必要的最起码的标准。本章将阐述用于结构完整的XML和HTML文档的规则。请特别注意XML与HTML的区别。

 

      本章的主要内容包括:

      * XML文档的组成

      * 置标和字符数据

      * 独立文档中的结构完整的XML

      * 结构完整的HTML

      6.1 XML文档的组成

      XML文档包含由XML标记和字符数据组成的文本。它是一个有固定长度的有序字节的集合,并遵守特定的约束。它可能是或者不是一个文件。例如,XML文档可能:

 

      * 存储在数据库中

      * 由CGI程序在内存中瞬间创建的

      * 由几个相互嵌套的不同文件组合而成

      * 不存在于自身的文件中

      但是如果把一个XML文档看作一个文件也是可以的,只要记住它可能并不是存在于硬盘上的真实文件。

      XML由称为"实体"的存储单元组成,每个实体包含文本或者二进制数据,但不能同时存在。文本数据由字符组成,二进制数据用于图片和小程序等类内容。用一个具体的示例说明就是,一个含有<IMG>标记的原始HTML文件是一个实体而不是文档。一个HTML文件加上所有使用<IMG>标记嵌入的图片就组成一个文档。

 

      在本章和后续几章中我们只针对由一个实体构成的简单的XML文档,即文档本身。而且这些文档只包含文本数据,不包含诸如图片小程序一类的二进制数据。这些文档能够完全独立被理解而无需读取其他文件。换句话说,它们是独立存在的。这种文档通常在它的XML标头中含有一个值为yes的standalone属性,如下所示:

 

<?xml version="1.0" standalone="yes"?

 

      外部实体和实体引用用于组合多个文件和其他数据源以创建一个独立的XML文档。这样的文档如果不引用其他文件就不能进行句法分析。这些文档通常在XML声明中含有一个属性值为no的standalone属性:

 

<?xml version="1.0" standalone="no"?>

 

      外部实体及实体引用将在第9章"实体与外部DTD子集"中讨论。

      6.2 置标和字符数据

      XML文档是文本。文本由字符组成。字符是字母、数字、标点符号、空格、制表符号或类似的东西。XML使用Unicode字符集(统一的字符编码标准集),它不仅包含来自英语和其他西欧字母表中的常见字母和符号,也包含来自古斯拉夫语、希腊语、希伯来语、阿拉伯语和梵语的字母表。另外还包含汉语和日语的象形汉字和韩国的Hangul音节表。在本章中只使用英语文本。

 

      国际化字符集将在第7章"外语和非罗马文本"中讨论。

      一个XML文档的文本可有两种用途,字符数据和置标。字符数据是文档的基本信息。另一方面,置标主要描述一个文档的逻辑结构。例如,回想一下第三章清单3-2中的greeting.xml:

 

 

<?xml version="1.0" standalone="yes"?>

<GREETING>

Hello XML!

</GREETING>

 

 

      其中<?xml version="1.0" standalone="yes"?>,<GREETING>和</GREETING>是置标。Hello

      XML!是字符数据。XML比其他格式优越的一点是它把实际数据与置标明显地分隔开。

      更确切地说,置标包括所有的注释、字符引用、实体引用、CDATA段定界符、标记、处理指令和DTD。其他的就是字符数据。但是文档被处理后,一些置标会变成字符数据。例如,置标>;变成了大于号(>)。文档经处理后留下的字符数据和所有的代表特定字符的数据称为可分析的字符数据。

6.2.1 注释

      XML的注释与HTML的注释很相似,它们以<!--开始,以-->结束。介于<!--和-->之间的全部数据均被XML处理器忽略,就像它们根本不存在一样。注释用于提醒自己或临时标注出文档中不完善的部分。例如:

 

<?xml version="1.0" standalone="yes"?>

<!--This is Listing 3-2 from The XML Bible-->

<GREETING>

Hello XML!

<!--Goodbye XML-->

</GREETING>

 

      在使用注释时必须遵循以下几条规则,大致如下:

      1. 注释不能出现在XML声明之前,XML声明必须是文档最前面的部分。例如,下面这种情况是不允许的:

 

<!--This is Listing 3-2 from The XML Bible-->

<?xml version="1.0" standalone="yes"?>

<GREETING>

Hello XML!

<!--Goodbye XML-->

</GREETING>

 

 

      2. 注释不能放在标记中,例如:下面这种情况是非法的:

 

<?xml version="1.0" standalone="yes"?>

<GREETING>

Hello XML!

</GREETING <!--Goodbye--> >

 

 

      3. 注释可以包围和隐藏标记。在下面例子中,<antigreeting>标记及其内容被当作注释;而且文档在浏览器中显示时不会出现,好像不存在一样:

 

<?xml version="1.0" standalone="yes"?>

<DOCUMENT>

<GREETING>

Hello XML!

</GREETING>

<!--

<ANTIGREETING>

Goodbye XML!

</ANTIGREETING>

-->

</DOCUMENT>

 

 

      由于注释有效地删除了文本的一些部分,必须保证剩余的文本仍然是一个结构完整的XML文档。例如,在没有注释掉相应的结束标记前千万不要注释掉起始标记。例如,下面的语句是非法的:

 

 

<?xml version="1.0" standalone="yes"?>

<GREETING>

Hello XML!

<!--

</GREETING>

-->

 

 

      一旦删除注释文本,剩余的是:

<?xml version="1.0" standalone="yes"?>

<GREETING>

Hello XML!

 

      因为<GREETING>标记没有与之匹配的结束标记</GREETING>,这已经不再是一个结构完整的XML文档。

      4. 两个连字符号(--)除了作为注释起始和结束标记的一部分外,不能出现在该注释中。例如,下面的是非法注释:

<!--The red door--that is,the second one--was left open-->

 

      这意味着不能像下面的语句这样嵌套注释:

 

<?xml version="1.0" standalone="yes"?>

<DOCUMENT>

<GREETING>

Hello XML!

</GREETING>

<!--

<ANTIGREETING>

<!--Goodbye XML!-->

</ANTIGREETING>

-->

</DOCUMENT>

 

 

      这也意味着如果注释掉带有表达式如i--或numberLeft--的C、Java或者JavaScript源代码时就会出现问题。通常只要意识到这个问题就不难解决。

6.2.2 实体引用 
      实体引用是指分析文档时会被字符数据取代的置标。XML预先定义了5个实体引用,列在表6-1中。实体引用用于XML文档中的特殊字符,否则这些字符将被解释为置标的组成部分。例如,实体引用&lt;代表小于号(<),否则会被解释为一个标记的起始部分。 
 
      表6-1 XML预定义的实体引用 
      实体引用 
      字 符 
&amp; 
& 
&lt; 
< 
&gt; 
> 
&quot; 
" 
&apos; 
 
      XML中的实体引用与HTML中不同,必须以一个分号结束。因此&gt;是正确的实体引用写法,&gt是不正确的。 
      未经处理的小于号(<)同表示“和”的符号(&)在一般的XML文本中往往被分别解释为起始标记和实体引用(特殊文本是指CDATA段,将在后面讨论)。因此,小于号同"和"号必须分别编码为&lt;和&amp;。例如,短语"Ben 
      & Jerry s New York Super Fudge Chunk Ice Cream"应当写成Ben &amp;Jerry s New 
      York Super Fudge Chunk Ice Cream 。 
      大于号、双引号和撇号在它们可能会被解释成为置标的一部分时也必须编码。但是,养成全部编码的习惯要比努力推测一个特定的应用是否会被解释为置标容易得多。 
      实体引用也能用于属性值中。例如: 
 
<PARAM NAME="joke" VALUE="The diner said,
&quote;Waiter,There&apos;s a fly in my soup!&quote;">
</PARAM>
 
 
      6.2.3 CDATA 
      大多数情况下,出现在一对尖括号(<>)中的是置标,不在尖括号中的是字符数据。但是有一种情况例外,在CDATA段中所有文本都是纯字符数据。看起来与标记或者实体相似的仅仅是它们各自相应的文本。XML处理器无论如何是不会解释它们的。 
 
      CDATA段用于需要把整个文本解释为纯字符数据而并不是置标的情况。当有一个包含许多<、>、&或"字符而非置标的庞大文本时,这是非常有用的。对于大部分C和Java源代码,正是这种情况。 
 
      如果想使用XML写有关XML的简介,CDATA段同样非常有效。例如,在本书中包含许多小的XML代码块,而我正在使用的字处理器又不能顾及这些情况。但是如果把本书转换为XML,我将不得不很辛苦地用&lt;代替全部小于号,&amp;代替所有"和"字符。如下所示: 
 
 
&lt;?xml version="1.0" standalone="yes"?&gt;
&lt;GREETING&gt;
Hello XML!
&lt;/GREETING&gt;
 
 
      为了避免这种麻烦,可以使用一个CDATA段表示一个不需翻译的文本块。CDATA段以结束,例如: 
 
<![CDATA[
<?xml version="1.0" standalone="yes"?>
<GREETING>
Hello XML!
</GREETING>
]]>
 
 
      唯一不许出现在CDATA段中的文本是CDATA的结束界定符]]>。注释可能会出现在CDATA段中,但不再扮演注释的角色。也就是说两个注释标记和包含在它们之间的全部文本都将显示出来。 
 
      因为]]>不能出现在CDATA段中,所以CDATA段不能嵌套。这使得使用XML写有关的CDATA段相对困难些。如果需要的话,必须去掉项目符号,并使用&lt;、&amp;和实体引用。 
 
      CDATA段不常需要,一旦需要时,它是非常有用的。

6.2.4 标记

      置标能够区分XML文件与无格式文本文件。置标的最大部分是标记。

      简而言之,标记在XML文档中以<开始,以>结束,而且不包含在注释或者CDATA段中。因此,XML标记有与HTML标记相同的形式。开始或打开标记以<开始,后面跟有标记名。终止或结束标记以</开始,后面也跟标记名。遇到的第一个>该标记结束。

 

      6.2.4.1 标记名

      每个标记都有一个名称。标记名必须以字母或下划线(_)开始,名称中后续字符可以包含字母、数字、下划线、连字符和句号。其中不能含有空格(经常用下划线替代空格)。下面是一些合法的XML标记:

 

 

<HELP>

<Book>

<volume>

<heading1>

<section.paragraph>

<Mary_Smith>

<_8ball>

 

 

      冒号出现在标记名中从语法上讲是合法的,但是它们被保留用于命名域。命名域可以混合和匹配可能使用同名标记的标记集合。命名域将在18章讨论

      以下是句法不正确的XML标记:

 

<Book%7>

<volume control>

<1heading>

<Mary Smith>

<.employee.salary>

 

 

      事实上标记名的规则也适用于其他许多名称,如属性名、ID属性值、实体名和其他一些将在后面几章遇到的结构。

      结束标记与起始标记同名,只是在起始尖括号后加了一个/。例如,如果起始标记是<FOO>,那么结束标记是</FOO>。下面是前面所提到的合法起始标记所对应的结束标记:

 

 

</HELP>

</Book>

</volume>

</heading1>

</section.paragraph>

</Mary_Smith>

</_8ball>

 

 

      XML名称是大小写敏感的。在HTML中的<P><p>是同一个标记,</p>可以结束一个<P>标记,但在XML中却不行。下面所示的并不是我们讨论过的合法起始标记所对应的结束标记:

 

</help>

</book>

</Volume>

</HEADING1>

</Section.Paragraph>

</MARY_SMITH>

</_8BALL>

 

      尽管大小写字母均可以用在XML的标记中,从此观点出发,我会尽可能遵循使用大写的约定。这主要是因为大写在本书中可以更突出,但是有时使用的标记集是别人建立的,那么采用别人的习惯约定是必要的。

 

      6.2.4.2 空标记

      许多不含数据的HTML标记没有结束标记。例如,在HTML中没有</LI></IMG></HR></BR>标记。一些页面作者在所列的项目后面确实会包含</LI>标记,一些HTML工具也使用</LI>标记。但是HTML

      4.0标准特别否认了这一点的必要性。同HTML中所有没有被公认的标记一样,一个不必要的</LI>的出现对交付的输出没有任何影响。

      这在XML中不是问题。XML的总体观点就是在分析文档时允许发现新的标记。因此没有识别的标记就不会被简单地忽略。而且XML处理器一定能够判明以前从没出现过的一个标记有没有结束标记。

 

      XML区分带有结束标记的标记,而不带结束标记的标记称为空标记。空标记以斜杠和一个结束尖括号(/>)结束。例如,<BR/><HR/>

      目前的Web浏览器处理这种标记的方法不一致,如果希望保持向后的兼容性,可以用结束标记来代替,只要在两个标记之间不包含任何文本。例如:

<BR></BR>

<HR></HR>

<IMG></IMG>

 

      在学了后续几章中的DTD和样式单后,将会看到在必须由传统浏览器分析的文档中使用HTML可以有多种方法保持向前和向后的兼容性。

6.2.5 属性

      在前面的章节中讨论过,起始标记和空标记可以随意地包含属性。属性是用等号(=)分隔开的名称-数值对。例如:

 

<GREETING LANGUAGE="English">

Hello XML!

<MOVIE SRC="WavingHand.mov"/>

</GREETING>

 

 

      在此<GREETING>标记有一个LANGUAGE属性,其属性值是English<MOVIE>标记有一个SRC属性,其属性值为WavingHand.mov

 

      6.2.5.1 属性名

      属性名是字符串,遵循与标记名同样的规则。这就是,属性名必须以字母或下划线(_)开始,名称中后续字符可以包含字母、数字、下划线、连字符和句号。其中不能含有空格(经常用下划线替代空格)。

 

      同一个标记不能有两个同名的属性。例如,下面的例子是不合法的:

<RECTANGLE SIDE="8cm" SIDE="10cm"/>

 

      属性名是区分大小写的。SIDE属性与side或者Side属性不是同一个属性,因此以下例子是合法的:

<BOX SIDE="8cm" side="10cm" Side="31cm"/>

 

      但是上面的这种写法很迷惑人,最好不要这样书写。

      6.2.5.2 属性值

      属性值也是字符串。如下面所示的LENGTH属性,即使字符串表示的是一个数,但还是两个字符72,不是十进制数的72

<RULE LENGTH="72"/>

 

      如果编写处理XML的代码,在对字符串执行算术运算之前必须把它们转换为一个数。

      与属性名不同,对属性值包含的内容没有任何限制。属性值可以包含空格,可以以一个数字或任何标点符号(有时单括号和双括号除外)开头。

      XML属性值由引号界定。与HTML属性不同,XML属性值必须加引号。大多数情况下是使用双引号,但是如果属性值本身含有一个引号,就需要使用单引号。例如:

 

<RECTANGLE LENGTH= 7" WIDTH= 8.5" />

 

      如果属性值中含有两种引号,那么其中不用于界定字符串的一个必须用合适的实体引用代替。我通常替换两个,这种方法很管用。例如:

<RECTANGLE LENGTH= 8&apos;7&quot; WIDTH="10&apos;6&quot;"/>

6.3 独立文档中结构完整的XML

      尽管可以根据需要编写标记,XML文档为了保持结构完整必须遵循一定的规则。如果一个文档不是结构完整的,大部分读取和显示操作都会失败。

      事实上,XML规范严格禁止XML句法分析器分析和解释结构欠妥的文档。正在执行操作的分析器唯一能做的是报告出错。它不会修改错误,不会作最大的努力显示作者想要的东西,也不会忽略不当的结构欠妥的标记。它所能做的是报告错误和退出。

 

      这样做的目的是为了避免对错误的兼容性的竞争。这种竞争已使得编写HTML语法分析程序和显示程序变得非常困难。因为Web浏览器承认畸形的HTML,而Web页面设计者不会特别尽力确保他们的HTML正确无误。事实上,他们甚至利用个别浏览器中的错误达到特殊的效果。为了正确显示被大量安装的HTML页面,每个新的Web浏览器必须支持已有的Web浏览器的每一个细微差别和各自的属性。用户将放弃任何一种严格执行HTML标准的浏览器。正是为了避免这种遗憾,XML处理器才只接受结构完整的XML

 

      为了使一个文档结构完整,XML文档中的所有置标和字符数据必须遵守前几节中给出的规则。而且有几条关于如何把置标和字符数据相互联系起来的规则。这些规则总结如下:

 

      1.文档的开始必须是XML声明。

      2.含有数据的元素必须有起始标记和结束标记。

      3.不含数据并且仅使用一个标记的元素必须以/>结束。

      4.文档只能包含一个能够包含全部其他元素的元素。

      5.元素只能嵌套不能重叠。

      6.属性值必须加引号。

      7.字符<&只能用于起始标记和实体引用。

      8.出现的实体引用只有&amp;&lt;&gt;&apos;&quot;

      这八条规则稍加调整就能适用于含有一个DTD的文档,而且对于定义文档与它的DTD之间关系的完整性有另外的规则。我们将在后面几章中介绍。现在请仔细看这些用于没有DTD文档的规则。

 

      DTD将在本书第二部分中讨论。

      #1:文档必须以XML声明开始

      下面是XML 1.0中独立文档的XML声明:

<?xml version="1.0" standalone="yes"?>

 

      如果声明出现,它绝对是该文件最开头部分,因为XML处理器会读取文件最先的几个字节并将它与字符串

 

      UTF-8Unicode的变种在第7"外语和非罗马文本"中讨论。

      XML确实允许完全省略XML声明。通常不推荐这样做,但这样做有特殊的用途。例如,省略XML声明,通过连接其他结构完整的XML文档有助于重新建立一个结构完整的XML文档。这种方法将在第9章讨论。而且,本章后面将要讲述的一种样式能够编写结构完整的HTML文档。

 

      #2:在非空标记中使用起始和结束标记

      如果忘了结束HTML的标记,Web浏览器并不严格追究。例如,如果文档包含一个<B>标记却没有相应的</B>标记,在<B>标记之后的全部文档将变为粗体。但文档仍然能显示。

 

      XML不会如此宽松,每个起始标记必须以相应的结束标记结束。如果一个文档未能结束一个标记,浏览器或移交器将报告一个错误信息,并且不会以任何形式显示任何文档的内容。

 

      #3:用"/>结束空标记

      不包含数据的标记,例如HTML<BR><HR><IMG>,不需要结束标记。但是XML空标记必须由/>结束,而不是>。例如<BR><HR><IMG>XML等价物是<BR/><HR/><IMG/>

 

      当前的Web浏览器处理这种标记的方法不一致。但是如果想保持向后的兼容性,可以使用结束标记来代替,而且不能在其间包含任何文本。例如:

<BR></BR>

<HR></HR>

<IMG></IMG>

 

      即使这样,Netscape处理<BR></BR>也有困难(它把这两个标记解释为行间距,而不是前面所讲的)。因此,在HTML中包含结构完整的空标记也并非总是可行的。

#4:让一个元素完全包含其他所有元素

      一个XML文档包含一个根元素,它完全包含了文档中其他所有元素。有时候这种元素被称作文档元素。假设根元素是非空的(通常都是如此),它肯定有起始标记和结束标记。这些标记可能使用但不是必须使用rootDOCUMENT命名。例如,在下面的文档中根元素是GREETING

 

<?xml version="1.0" standalone="yes"?>

<GREETING>

Hello XML!

</GREETING>

 

      XML声明不是一个元素,它更像是一个处理指令,因此不必包含在根元素中。类似地,在一个XML文档中的其他非元素数据,诸如其他处理指令、DTD和注释也不必包含在根元素中。但是所有实际的元素(除根元素本身)必须包含在根元素中。

 

      #5:不能重叠元素

      元素可以包含别的元素(大多数情况下),但是元素不能重叠。事实上是指,如果一个元素含有一个起始标记,则必须同时含有相应的结束标记。同样,一个元素不能只含有一个结束标记而没有相应的起始标记。例如,下面的XML是允许的:

 

<PRE><CODE>n =n +1;</CODE></PRE>

 

      下面所示的XML是非法的,因为结束标记</PRE>放在了结束标记</CODE>之前:

<PRE><CODE>n =n +1;</PRE></CODE>

 

      大部分HTML浏览器容易处理这种情况,但是XML浏览器会因为这种结构而报告出错。

      空标记可随处出现。例如:

<PLAYWRIGHTS>Oscar Wilde<HR/>Joe Orton</PLAYWRIGHTS>

 

      本规则与规则4联系在一起有如下含义:对于所有非根元素,只能有一个元素包含某一非根元素,但是元素不能包含其中含有非根元素的元素。这个直接含有者称为非根元素的父元素,非根元素被认为是父元素的子元素。因此,每个非根元素只有一个父元素。但是一个单独的元素可以有任意数目的子元素或含有子元素。

 

      请分析如下所示的清单6-1。根元素是DOCUMENT元素,它含有两个元素。第一个STATE元素含有4个子元素:NAMETREEFLOWERCAPITOL。第二个STATE元素含有3个子元素:NAMETREECAPITOL。这些里层的子元素只包含字符数据,没有子元素。

 

      清单6-1:父元素和子元素

 

<?xml version="1.0" standalone="yes"?>

<DOCUMENT>

<STATE>

<NAME>Louisiana</NAME>

<TREE>Bald Cypress</TREE>

<FLOWER>Magnolia</FLOWER>

<CAPITOL>Baton Rouge</CAPITOL>

</STATE>

<STATE>

<NAME>Mississippi</NAME>

<TREE>Magnolia</TREE>

<CAPITOL>Jackson</CAPITOL>

</STATE>

</DOCUMENT>

 

 

      在编程人员的术语中,这意味着XML文档形成了一个树。图6-1显示了清单6-1表示的树形结构以及将该结构称为树的原因。图6-1从根开始,逐级地分支延伸到树末端的叶。

 

      树有一些好的特性使计算机程序易于读取,尽管对于文档的作者而言是无关紧要的。

      6-1 清单6-1表示的树形结构

      树通常由上向下画,这就是说树的根应该在图片的顶部而不是底部。但这样看起来不像真正的树,不过并不影响数据结构的拓扑形式。

      #6:属性值必须加引号

      XML要求所有的属性值必须加引号,不管属性值是否包括空白。例如:

<A HREF="http://metalab.unc.edu/xml/">

 

      HTML的要求则不是这样。比如,HTML允许标记含有不带引号的属性。例如,下面是一个合法的HTML<A>标记:

<A HREF=http://metalab.unc.edu/xml/>

 

      唯一的要求是属性值不能嵌有空格。

      如果一个属性值本身含有双引号,可以使用属性值加单引号来代替。例如:

      <IMG SRC="sistinechapel.jpg" ALT= And God said,"Let there be light," and

      there was light />

      如果一个属性值包含有单引号和双引号,可以使用实体引用&apos;代替单引号,"代替双引号。例如:

<PARAM name="joke" value="The diner said,

&quot;Waiter,There&apos;s a fly in my soup!&quot;">

 

      #7:只在开始标记和实体引用中使用<&

      XML假定最先的<是一个标记的开始,&是一个实体引用的开始(HTML也是如此,如果省略它们,大部分浏览器会假定有一个分号)。例如:

<H1>A Homage to Ben &Jerry s

New York Super Fudge Chunk Ice Cream</H1>

 

      Web浏览器会正确地显示该标记,但是为了最大限度的安全,应当避免使用&,用&来代替,像下面这样:

<H1>A Homage to Ben &amp;Jerry s New York Super Fudge Chunk

Ice Cream</H1>

 

      开尖括号(<)的情况也类似。请看下面很普通的一行Java代码:

<CODE> for (int i =0;i <=args.length;i++){</CODE>

 

      XMLHTML都会把<=中的小于号当作一个标记的开始。该标记会延续到下一个>。因此该行会现示成

      for (int i =0;i

      而不是:

for (int i =0;i <=args.length;i++){

=args.length;i++){"被解释成一个不能识别的标记的一部分。

 

      把小于号写成<lt;可以出现在XMLHTML文本中。例如:

<CODE> for (int i =0;i &lt;=args.length;i++){</CODE>

 

      结构完整的XML要求把&写成&amp;,把<写成>lt;,只要不是作为标记或者实体的一部分时都应如此。

      #8:只能使用现有的5个实体引用

      读者可能已经熟悉了几个HTML中的实体引用,例如&copy;为插入版权号,&reg为插入注册商标号。但是除了已经讨论过的五个实体引用,XML只能使用预先在DTD中定义过的实体引用。

 

      但是现在读者可能还不了解DTD,如果与字符&出现在文档中的任何地方,其后必须紧跟amp;lt;gt;apos;或者quot;。所有其他的用法均会破坏结构完整性。

 

      在第9"实体和外部DTD子集"中将会学习如何使用DTD定义插入特殊符号和大块样板文本的新实体引用。

6.4 结构完整的HTML

      即使在大部分Web浏览器还不能直接支持XML的情况下,也可以通过编写结构完整的HTML来练习XML技巧。这就是遵守XML的结构完整性约束,但只是使用标准的HTML标记的HTML。结构完整的HTML比多数人和FrontPage等工具编写的标准的HTML容易读取,同时容易被Web机器人中用动检索引擎理解。它更为强健,对它作一些改动不会破坏它,在转移的过程中不会因为变换浏览器和操作平台对它产生影响。而且可以使用XML工具服务于HTML文档,这对于那些服务器不支持XML的读者来说仍然保持了向后的兼容性。

 

      6.4.1 现实的Web页面存在的问题

      真正的Web页面非常标准。没有结束标记,元素重叠,在页面中包含未经处理的小于号,忽略实体引用后面的分号。存在这些问题的Web页面严格来讲是无效的,但部分浏览器能接受它们。不过如果校正了这些问题,页面将会更整洁,显示更快,更容易维护。

 

      Web页面包含的一些常见的问题:

      1. 起始标记没有对应的结束标记(没有结束元素)

      2. 结束标记没有相应的起始标记

      3. 元素重叠

      4. 属性值未加引号

      5. 没有避免使用<>&"符号

      6. 没有根元素

      7. 结束标记与起始标记不匹配

      清单大致按照其重要性排列,但确切的细节因标记不同而变化。例如,没有被结束<STRONG>标记会把跟随其后的所有元素变为粗体。但是没有被结束<LI>或者<P>标记不会引发任何问题。

 

      有几条规则仅适用于XML文档,如果试图把它们汇集在已存在的HTML页面中,确实会带来问题。这些规则有:

      1.以一个XML声明开始

      2.空标记必须以/>结束

      3.使用的实体引用只有&<>&apos;"

      校正这些问题并不难,只是有几个稍不注意就会出现问题。下面让我们仔细加以研究。

      6.4.1.1 结束所有的起始标记

      任何含有内容的元素,无论是文本还是别的子元素,应该有一个起始标记和结束标记。HTML不绝对要求这样做。例如<P><DT><DD><LI>经常被单独使用。但是,这样做主要依靠Web浏览器能够很好地判断一个元素的结束位置,浏览器并不总能确切地按照作者的意愿去做。因此最好是明确地结束所有起始标记。

 

      对于编写HTML的方法,这里要求对其所作的最大改变是把<P>看作一个容器,而不是一个简单的段落分界符。例如,以前格式化Federalist

      Papers的开始部分,如下所示:

 

To the People of the State of New York:

<P>

AFTER an unequivocal experience of the inefficiency of the

subsisting federal government,you are called upon to

deliberate on a new Constitution for the United States of

America.The subject speaks its own importance;comprehending

in its consequences nothing less than the existence of the

UNION,the safety and welfare of the parts of which it is

composed,the fate of an empire in many respects the most

interesting in the world.It has been frequently remarked that

it seems to have been reserved to the people of this country,

by their conduct and example,to decide the important question,

whether societies of men are really capable or not of

establishing good government from reflection and choice,or

whether they are forever destined to depend for their political

constitutions on accident and force.If there be any truth in

the remark,the crisis at which we are arrived may with

propriety be regarded as the era in which that decision is to

be made;and a wrong election of the part we shall act may,in

this view,deserve to be considered as the general misfortune

of mankind.

<P>

结构完整性要求将上面语句格式化为:

<P>

To the People of the State of New York:

</P>

<P>

AFTER an unequivocal experience of the inefficiency of the

subsisting federal government,you are called upon to

deliberate on a new Constitution for the United States of

America.The subject speaks its own importance;comprehending

in its consequences nothing less than the existence of the

UNION,the safety and welfare of the parts of which it is

composed,the fate of an empire in many respects the most

interesting in the world.It has been frequently remarked that

it seems to have been reserved to the people of this country,

by their conduct and example,to decide the important question,

whether societies of men are really capable or not of

establishing good government from reflection and choice,or

whether they are forever destined to depend for their political

constitutions on accident and force.If there be any truth in

the remark,the crisis at which we are arrived may with

propriety be regarded as the era in which that decision is to

be made;and a wrong election of the part we shall act may,in

this view,deserve to be considered as the general misfortune

of mankind.

</P>

 

      你以前学过的可能是把<P>看作一个段落的结束,现在应当把它看作一个开始。这会带来一些好处,例如可以方便地为一个段落指定多种格式化属性。例如,下面是可在http://thomas.loc.gov/home/hres581.html上看到的

      House Resolution 581的原始HTML标题:

 

<center>

<p><h2>House Calendar No.272</h2>

<p><h1>105TH CONGRESS 2D SESSION H.RES.581</h1>

<p>[Report No.105-795 ]

<p><b>Authorizing and directing the Committee on the

Judiciary to investigate whether sufficient grounds

exist for the impeachment of William Jefferson Clinton,

President of the United States.</b>

</center>

 

 

      下面是同样的文本,但使用的是结构完整的HTMLAlign属性代替了相应的center元素,并且使用CSS样式属性代替了<b>标记。

 

<h2 align="center">House Calendar No.272</h2>

<h1 align="center">105TH CONGRESS 2D SESSION H.RES.581</h1>

<p align="center">[Report No.105-795 ]</p>

<p align="center" style="font-weight:bold">

Authorizing and directing the Committee on the Judiciary to

investigate whether sufficient grounds exist for the

impeachment of William Jefferson Clinton,

President of the United States.

</p>

6.4.1.2 删除孤立的结束标记并且不要使元素重叠

      在编辑页面时,删除一个起始标记而忘了删除相应的结束标记,这种情况很常见。在HTML中,一个孤立的结束标记如</STRONG>或者</TD>没有任何相匹配的起始标记不会引发问题。但是这样会使文件比需要的更长,下载速度变慢,而且潜在地使人或工具理解和编辑HTML源文件发生混淆。因此应当确保每个结束标记都有正确的起始标记。

 

      但是结束标记没有任何起始标记往往意味着那些元素错误地重叠了。在Web页面上的大部分重叠元素很容易修改。例如下面这种常见的错误:

 

<B><I>This text is bold and italic</B></I>

 

      I元素在B元素中开始,也必须在B元素中结束。需要做的只是交换两个结束标记的位置:

<B><I>This text is bold and italic</I></B>

 

      同样可以交换两个起始标记:

<I><B>This text is bold and italic</B></I>

 

      偶尔会遇到一个棘手的问题。例如,下面是来自白宫主页的一个片段(http//www.whitehouse.gov/1998114日)。其中已醒目地标出了有问题的标记,很容易看出错误所在:

 

 

<TD valign=TOP width=85>

<FONT size=+1>

<A HREF="/WH/New"><img border=0

src="/WH/images/pin_calendar.gif"

align=LEFT height=50 width=75 hspace=5 vspace=5></A><br></TD>

<TD valign=TOP width=225>

<A HREF="/WH/New"><B>What 's New:</B></A><br>

</FONT>

What's happening at the White <nobr>House -</nobr><br>

<font size=2><b>

<!-- New Begin -->

<a href="/WH/New/html/19981104-12244.html">Remarks Of The

President Regarding Social Security</a>

<BR>

<!-- New End -->

</font>

</b>

</TD>

 

 

      其中,<FONT size=+1<元素在第一个<TD valign=TOP

      width=85>元素中开始,但是它的后续部分越过该元素结束于另一个<TD valign=TOP

      width=225>元素中。在此情况下,正确的处理方法是在第一个</TD>结束标记之前立即结束<FONT

      size=+1>起始标记,然后在第二个TD元素开始之后立即添加一个新的<FONT size=+1>起始标记,如下所示:

 

<TD valign=TOP width=85>

<FONT size=+1>

<A HREF="/WH/New"><img border=0

src="/WH/images/pin_calendar.gif"

align=LEFT height=50 width=75 hspace=5 vspace=5></A><br>

</FONT></TD>

<TD valign=TOP width=225>

<FONT size=+1>

<A HREF="/WH/New"><B>What 's New:</B></A><br>

</FONT>

What 's happening at the White <nobr>House -</nobr><br>

<font size=2><b>

<!-- New Begin -->

<a href="/WH/New/html/19981104-12244.html">Remarks Of The

President Regarding Social Security</a>

<BR>

<!-- New End -->

</font>

</b>

</TD>

 

 

      6.4.1.3 给所有属性加引号

      HTML属性只有在含有空格时才需要加引号,即使含有引号对它也并无妨碍。而且使用引号有助于以后将属性值修改为含有空格的属性值。很容易忘记加引号,尤其对于<IMG>ALT这样的属性,在使用Web浏览器查看文档时它们的错误不很明显。

 

      例如下面的<IMG>标记:

<IMG SRC=cup.gif WIDTH=89 HEIGHT=67 ALT=Cup>

<xmp>

应将其改写为:

<xmp>

<IMG SRC="cup.gif" WIDTH="89" HEIGHT="67" ALT="Cup">

6.4.1.4 <>&必须转义

      HTML对小于号和与号的要求比XML宽松得多。即使这样,在纯HTML文本中它们确实也会引起麻烦,特别是在它们直接跟有其他字符时。例如,考虑下面来自Eudora软件中的From:标题中的email地址在经过复制和粘贴后显示的样子:

 

      Elliotte Rusty Harold <elharo@metalab.unc.edu>

      如果用HTML来显示的话,看到的可能是:

      Elliotte Rusty Harold

      elharo@metalab.unc.edu无意间被尖括号隐藏了。如果想在HTML中包含原始的小于号和与号,应当使用<&代替。其正确的HTML形式是:

 

      Elliotte Rusty Harold <elharo@metalab.unc.edu>

      没有转义的大于号带来的问题不易察觉,如果在它之前有一个未结束的标记,它会被解释为一个置标。文档中会出现这种没有完成的标记,而且附近的大于号会掩盖它们的存在。例如下面的一段Java代码:

 

for (int i=0;i<10;i++){

for (int j=20;j>10;j--){

 

      这很可能显示为:``

      for (int i=0;i10;j--){

      如果这只是一个100行程序中的两行,在随便校正时极有可能错过这种疏忽。另一方面,如果转义了大于号,而未转义小于号将会隐藏程序的其余部分,而且这种问题容易被发现。

 

      6.4.1.5 使用一个根元素

      用于HTML文件的根元素被假定为html。大部分浏览器允许不包含它的这种疏忽。尽管如此,最好把<html>作为文档的第一个标记,</html>作为文档的最后一个标记。如果其他文本或置标出现<html>之前或</html>之后,应把它们移到<html></html>之间。

 

      这个问题常见的形式是忘记在文档的结尾包括</html>。我通常先键入<html></html>,然后在它们之间键入其他内容,而不是在编写完整文档再加</html>标记,指望着几天后还会记得应该加上</html>标记。

 

      6.4.1.6 所有标记使用相同的大小写形式

      HTML对大小写不敏感,XML则不然。应推荐给标记挑选一个唯一的大小写形式,要么都大写,要么都小写,并且贯穿全文。这样做比记住每一个标记的细节要简单。我通常选小写,因为它比较容易输入。而且W3CHTML再现为XML应用程序的结果也使用这个格式。

 

      在第20章中读取文档类型定义一节将详细描述HTML再现为XML。但是必须停止更深的探讨,因为这项工作使用在后面几章中学不到的技巧。

      6.4.1.7 "/>"结束空标记

      HTML转换成结构完整的XML,其中最令人讨厌的就是空标记。HTML在形式上不能识别XML<elementname/>空标记句法。虽然很容易将<br>转换为<br/><hr>转换为<hr/><img>转换为<img/>,但是给定的浏览器是否会正确显示变换后的标记是一个未知数。

 

      不要把<br><hr><img>这样真正的空元素与标准的HTML中只有一个起始标记但能够带有内容的标记混淆,如<p><li><dt>,和<dd>

 

      一个被XML规范认可的最简单的解决办法是用含有内容的起始和结束标记对替换空标记。浏览器将忽略该不能识别的结束标记,请看下面的实例:

<br></br>

<hr></hr>

<IMG SRC="cup.gif" WIDTH="89" HEIGHT="67" ALT="Cup"></IMG>

 

      在实践中这样做确实没有什么问题,但有一个明显的例外。Netscape

      4.5以及更早的版本把</br><br>看成是一样的,当作一个换行符号。因此<br>是单个换行符号,<br></br>则是一对换行符号,实际上更像一个段落标记。而且,Netscape完全忽略<br/>。必须支持传统浏览器的Web站点(几乎是所有的Web站点)不能使用<br></br>或者<br/>。在实践中对于XML和传统浏览器都适用的解决办法如下:

 

      <br />

      请注意<br/>之间的空格,确实解释不了这样为什么管用,而其他更多的变化却

      不行。如果你确实想使用结构完整的HTML,我所能做的只是提供可能奏效的解决办法。

6.4.1.8 只使用&<>&apos;"实体引用

      许多Web页面除了&<>&apos;、和"之外确实不需要更多的实体引用。但是HTML 4.0中规定了许多:

      * &#8482;为商标号(tm)

      * &copy;为版权号(c)

      * ∞为无穷大号∞

      * π为小写的希腊字母pi,π

      还有几百个别的实体引用,但是使用任何一个将破坏文档的结构完整性。解决这个问题的方法是使用一个DTD。我们将在第9章中讨论DTD对实体引用的影响。同时下面有几个暂时的解决办法。

 

      最简单的办法是以某一字符集编码一个包含全部所需符号的文档,然后使用一个<META>指令指定正在使用的字符集。例如,指定文档使用UTF-8编码(一个字符集,包含了几乎全部可能用到的字符,将在第7章讨论),而且应当把它放到文档的开头。

 

<META http-equiv="Content-Type"

content="text/html;charset=UTF-8">

 

      或者可以简单地告诉Web服务器,让它提供必要的内容类型标题。通常使用<META>标记要简单一些:

      Content-Type:text/html;charset=UTF-8

      采用这种方法的问题是许多浏览器不一定能显示UTF-8字符集。对于其他提供所用的特殊字符的字符集也一样。

      HTML 4.0支持XML中的字符实体引用。这就是说可以使用&#后跟Unicode中字符的十进制或者十六进制代码来代替一个字符。例如:

      * &#8482; 为商标号(tm)

      * &#169; 为版权号(c)

      * &#8734; 为无穷大号∞

      * &#960; 为小写的希腊字母pi,π

      HTML 3.2只正式支持界于0255ISO Latin-1)之间的数字字符引用,而Navigator 4.0和以后的版本以及Internet

      Explorer能识别更多的Unicode字符。

      如果确实需要一个结构完整的向后与HTML兼容的XML,可以把这些字符作为内联图片。例如:

* <img src="tm.gif" width="12" height="12" alt="TM"></img>

  </img>商标号(tm)

* <img src="copyright.gif" width="12" height="12" lt="Copyright">

  a,为版权号(c);

* <img src="infinity.gif" width="12" height="12" alt="infinity"></img>

  无穷大号∞;

* <img src="pi.gif" width="12" height="12" alt="pi"></img>

  小写的希腊字母pi,π。

 

      事实上,我不赞成使用这种方法。结构完整性在HTML中并不太重要,它只是强制读者增加了下载和显示出来的时间。

      6.4.1.9 XML声明

      HTML文档不需要XML声明,但有也无妨。Web浏览器只忽略它们不承认的标记。从这一点看,下面这一行就是另外一个标记:

<?xml version="1.0" standalone="yes"?>

 

      因为不懂XML的浏览器解释不了<?xml?>标记,它们会简单地忽略它。懂得XML的浏览器会把它当作一个提示,表明该文档是结构完整的XML文档,并按此处理它。

 

      遗憾的是,不完全懂得XML的浏览器分析这些句法非常困难。特别是Internet Explorer 4.0 for the

      Mac不是指Netscape

      Navigator或者其他版本的IE把它当作下载一个文件的信号,而不作显示。因此,不得不从Web页面中将XML声名删除。

6.4.1.10 遵循规则

      按照本章描述的规则编写结构完整的XML文档不是特别困难,但是XML浏览器对于标准的句法不像HTML浏览器那样宽容,因此要细心编写。

      如果违反了任何结构完整性约束,XML分析器和浏览器将报告一个句法错误。因此编写XML的过程与用某种编程语言编写代码的过程有些相似,首先编写,然后编译,如果编译失败再根据报告的错误修改。

 

      通常在能够看到完成的文档之前要经过几次从编辑到编译的过程。而且编写XML文档比编写CJava源代码要容易得多,只要很少的练习就会达到只出现相当少的错误,编写XML的速度几乎与你输入的速度一样快。

 

      6.4.2 HTML整理工具

      有几种工具能够帮助我们整理页面。最引人注目的是来自XML.COMRUWFAre You Well Formed?)和由W3CDave

      Raggett编写的HTML Tidy

      6.4.2.1 RUWF

      任何能够检验XML文档结构完整性的工具同样能够检验结构完整的HTML文档。其中最容易使用的工具是来自XML.COMRUWF结构完整性检验程序。图6-2显示了该检验程序。只要键入想检验的页面的URLRUWF就会返回在页面上发现的几十个错误。

 

      6-2 RUWF结构完整性检验器

      下面是RUWF在白宫主页上找到的第一批错误。这些错误大部分是标准的XML,但是它们是合法的HTML。但至少有一处错误("55行,30列:</FONT>周围没有相应的起始标记”)对HTMLXML都是一个错误。

 

Line 28,column 7:Encountered </HEAD>expected </META>

...assumed </META>...assumed </META>...assumed </META>

...assumed </META>

Line 36,column 12,character '0 ':after AttrName=in start-tag

Line 37,column 12,character '0 ':after AttrName=in start-tag

Line 38,column 12,character '0 ':after AttrName=in start-tag

Line 40,column 12,character '0 ':after AttrName=in start-tag

Line 41,column 10,character 'A ':after AttrName=in start-tag

Line 42,column 12,character '0 ':after AttrName=in start-tag

Line 43,column 14:Encountered </CENTER>expected </br>

...assumed </br>...assumed </br>

Line 51,column 11,character '+':after AttrName=in start-tag

Line 52,column 51,character '0 ':after AttrName=in start-tag

Line 54,column 57:after &

Line 55,column 30:Encountered </FONT>with no start-tag.

Line 57,column 10,character 'A ':after AttrName=in start-tag

Line 59,column 15,character '+':after AttrName=in start-tag

 

      6.4.2.2 HTML Tidy

      一旦确定了问题就会想到要修改它们,许多常见的问题&#0;&#0;例如,给属性值加引号&#0;&#0;是能够自动被修改的。做这种修改最便利的工具是Dave

      Raggett的命令行程序HTML TidyTidy是用ANSI

      C写成的一个字符-模式程序,能够在许多操作平台如WindowsUnixBeOSMac系统上执行。

      Tidy在本书所附的CD-ROMutilities/tidy目录中,包含用于Windows

      NTBeOS的二进制代码和用于所有操作平台的可移植代码。可以从站点http://www.w3.org/People/Raggett/tidy/中下载最新版本的Tidy

 

      Tidy通过几种不同的方式整理HTML文件,它们并非都与XML结构完整性有关。事实上在默认模式下,Tidy倾向于删除不必要的结束标记(对HTML而言,不是对XML),像</LI>。并且对破坏结构完整性的地方作一些修改。但是可以使用-asxml开关指定需要结构完整的XML输出。例如,把index.html文件转换为结构完整的XML,需要从DOS窗口或者外壳提示符下输入:

 

      C:\>tidy -m -asxml index.html

      -m标志告诉Tidy就地转换文件。-asxml标志告诉Tidy把输出的文档格式转化为XML文档。

      6.5 本章小结

      在本章学习了如何编写结构完整的XML。主要包括以下内容:

      * XML文档是满足一定结构完整性标准的一连串字符

      * XML文档的文本分为字符数据和置标

      * 注释可为代码加上说明文字,可能是为了自己看的注释,也可能是通过注释将还没有写好的部分注释掉

      * 使用实体引用可以在文档中包含<>&"

      * CDATA段对于嵌有很多<>&字符的文档是很有用的

      * XML文档中的标记以<开始,并以>结束,而且不能出现在注释或者CDATA段中

      * 起始标记和空标记可以包含描述元素的属性

      * HTML文档稍加处理会变得结构完整

      在下一章将要讨论如何使用非英语语言编写XML,尤其是用与英语差别很大的语言。如阿拉伯语、汉语和希腊语。