XML教程

4 数据的结构化(2)

4.2.3 球员数据的XML

      每个球队是由球员组成的。每个球员都有姓和名。将姓和名分开是重要的,这样一来既可以根据名来分类也可以根据姓来分类。1998Yankees阵容第一个出场的投球手的数据可编码如下:

 

 

<TEAM>

<TEAM_CITY>New York</TEAM_CITY>

<TEAM_NAME>Yankees</TEAM_NAME>

<PLAYER>

<GIVEN_NAME>Orlando</GIVEN_NAME>

<SURNAME>Hernandez</SURNAME>

</PLAYER>

<PLAYER>

<GIVEN_NAME>David</GIVEN_NAME>

<SURNAME>Cone</SURNAME>

</PLAYER>

<PLAYER>

<GIVEN_NAME>David</GIVEN_NAME>

<SURNAME>Wells</SURNAME>

</PLAYER>

<PLAYER>

<GIVEN_NAME>Andy</GIVEN_NAME>

<SURNAME>Pettitte</SURNAME>

</PLAYER>

<PLAYER>

<GIVEN_NAME>Hideki</GIVEN_NAME>

<SURNAME>Irabu</SURNAME>

</PLAYER>

</TEAM>

 

 

      为了更明显起见,使用标记<GIVEN_NAME><SURNAME>比使用<FIRST_NAME><LAST_NAME>或者<FIRST_NAME><FAMILY_NAME>更好一些。由于不同国家的文化背景不同,可能名(given

      name)在先也可能姓(family name)在先。同时在所有的文化背景下,别号(surnames)不一定就是姓(family names)。

      4.2.4 球员统计数据的XML

      以下几个步骤提供了每个球员的统计数据。统计数据看起来对于投球手和击球手并没有一点不同,特别是对于American

      League联赛,这里没有几个投球员击过球。下面是Joe Girardi1998年的统计数据。他是一个接球手,因而我们使用击球的统计数据:

 

<PLAYER>

<GIVEN_NAME>Joe </GIVEN_NAME>

<SURNAME>Girard </SURNAME>

<POSITION>Catcher</POSITION>

<GAMES>78</GAMES>

<GAMES_STARTED>76</GAMES_STARTED>

<AT_BATS>254</AT_BATS>

<RUNS>31</RUNS>

<HITS>70</HITS>

<DOUBLES>11</DOUBLES>

<TRIPLES>4</TRIPLES>

<HOME_RUNS>3</HOME_RUNS>

<RBI>31</RBI>

<STEALS>2</STEALS>

<CAUGHT_STEALING>4</CAUGHT_STEALING>

<SACRIFICE_HITS>8</SACRIFICE_HITS>

<SACRIFICE_FLIES>1</SACRIFICE_FLIES>

<ERRORS>3</ERRORS>

<WALKS>14</WALKS>

<STRUCK_OUT>38</STRUCK_OUT>

<HIT_BY_PITCH>2</HIT_BY_PITCH>

</PLAYER>

 

 

      现在让我们看一下一个投球手的统计数据。虽然投球手在American League中很少击球,但在National

      League中却常常击球,到目前为止,投球手击球的次数还是比其他球员少。根据投球手的投球表现,雇用或解雇、表扬或批评。如果投球手偶尔击中一球,则会得到额外的奖励。投球的统计包括比赛场数(games

      played)、得胜场数(wins)、失败场数(losses)、投球局数(innings pitched)、得分(earned

      runs)、成功防守次数(shutouts)、击中数(hits against)、走步放弃(walks given

      up)和其他数据。下面是Hideki Irabu1998年统计数据的XML编码:

 

<PLAYER>

<GIVEN_NAME>Hideki</GIVEN_NAME>

<SURNAME>Irabu</SURNAME>

<POSITION>Start ng P tcher</POSITION>

<WINS>13</WINS>

<LOSSES>9</LOSSES>

<SAVES>0</SAVES>

<GAMES>29</GAMES>

<GAMES_STARTED>28</GAMES_STARTED>

<COMPLETE_GAMES>2</COMPLETE_GAMES>

<SHUT_OUTS> </SHUT_OUTS>

<ERA>4.06</ERA>

<INNINGS> 73</INNINGS>

<HOME_RUNS>148</HOME_RUNS>

<RUNS>27</RUNS>

<EARNED_RUNS>79</EARNED_RUNS>

<HIT_BATTER>78</HIT_BATTER>

<WILD_PITCHES>9</WILD_PITCHES>

<BALK>6</BALK>

<WALKED_BATTER>1</WALKED_BATTER>

<STRUCK_OUT_BATTER>76</STRUCK_OUT_BATTER>

</PLAYER>

 

 

      XML标记的简洁性不是太重要

      从整个示例来看,我已经遵循了XML的明显的原则:"Terseness in XML markup is of minimal

      importance."XML标记的简洁性不是太重要的。)这当然对非棒球文化下的读者很有帮助。这些读者对于棒球术语及其简写不是很熟悉。比如无法了解为什么

      walk的简写是 BB(base on balls)而不是人们以为的W。如果文档的大小是个问题的话,将文档用Zip一类的工具进行压缩还是很容易的。

      但是,这并不意味着XML是相当长的,也不意味着手工键入是非常枯燥无味的。我承认,本例强烈地吸引我使用简略语来书写,这样一来,清晰性就丧失殆尽。如果我使用简写,那么典型的PLAYER元素可能如下:

 

 

<PLAYER>

<GIVEN_NAME>Joe</GIVEN_NAME>

<SURNAME>Girard </SURNAME>

<P>C</P>

<G>78</G>

<AB>254</AB>

<R>31</R>

<H>70</H>

<DO>11</DO>

<TR>4</TR>

<HR>3</HR>

<RBI>3 </RBI>

<BB>14</BB>

<SO>38</SO>

<SB>2</SB>

<CS>4</CS>

<HBP>2</HBP>

</PLAYER>

4.2.5 XML组装在一起

      到目前为止,我向读者展示的只是一段一段(每段一个元素)的XML文档。但现在是该将各段组装在一起,看一看包括1998Major

      League赛季的统计数据的全部文档的时候了。清单4-1列出了完成了的XML文档,其中包括两个联赛、六个分部、三十个队和九个球员。

      清单4-1:一份完整的XML文档

 

<?xml version="1.0"?>

<SEASON>

<YEAR>1998</YEAR>

<LEAGUE>

<LEAGUE_NAME>National League</LEAGUE_NAME>

<DIVISION>

<DIVISION_NAME>East</DIVISION_NAME>

<TEAM>

<TEAM_CITY>Atlanta</TEAM_CITY>

<TEAM_NAME>Braves</TEAM_NAME>

<PLAYER>

<SURNAME>Malloy</SURNAME>

<GIVEN_NAME>Marty</GIVEN_NAME>

<POSITION>Second Base</POSITION>

<GAMES>11</GAMES>

<GAMES_STARTED>8</GAMES_STARTED>

<AT_BATS>28</AT_BATS>

<RUNS>3</RUNS>

<HITS>5</HITS>

<DOUBLES>1</DOUBLES>

<TRIPLES>0</TRIPLES>

<HOME_RUNS>1</HOME_RUNS>

<RBI>1</RBI>

<STEALS>0</STEALS>

<CAUGHT_STEALING>0</CAUGHT_STEALING>

<SACRIFICE_HITS>0</SACRIFICE_HITS>

<SACRIFICE_FLIES>0</SACRIFICE_FLIES>

<ERRORS>0</ERRORS>

<WALKS>2</WALKS>

<STRUCK_OUT>2</STRUCK_OUT>

<HIT_BY_PITCH>0</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Guillen</SURNAME>

<GIVEN_NAME>Ozzie </GIVEN_NAME>

<POSITION>Shortstop</POSITION>

<GAMES>83</GAMES>

<GAMES_STARTED>59</GAMES_STARTED>

<AT_BATS>264</AT_BATS>

<RUNS>35</RUNS>

<HITS>73</HITS>

<DOUBLES>15</DOUBLES>

<TRIPLES>1</TRIPLES>

<HOME_RUNS>1</HOME_RUNS>

<RBI>22</RBI>

<STEALS>1</STEALS>

<CAUGHT_STEALING>4</CAUGHT_STEALING>

<SACRIFICE_HITS>4</SACRIFICE_HITS>

<SACRIFICE_FLIES>2</SACRIFICE_FLIES>

<ERRORS>6</ERRORS>

<WALKS>24</WALKS>

<STRUCK_OUT>25</STRUCK_OUT>

<HIT_BY_PITCH>1</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Bautista</SURNAME>

<GIVEN_NAME>Danny</GIVEN_NAME>

<POSITION>Outfield</POSITION>

<GAMES>82</GAMES>

<GAMES_STARTED>27</GAMES_STARTED>

<AT_BATS>144</AT_BATS>

<RUNS>17</RUNS>

<HITS>36</HITS>

<DOUBLES>1</DOUBLES>

<TRIPLES>0</TRIPLES>

<HOME_RUNS>3</HOME_RUNS>

<RBI>17</RBI>

<STEALS>1</STEALS>

<CAUGHT_STEALING>0</CAUGHT_STEALING>

<SACRIFICE_HITS>3</SACRIFICE_HITS>

<SACRIFICE_FLIES>2</SACRIFICE_FLIES>

<ERRORS>2</ERRORS>

<WALKS>7</WALKS>

<STRUCK_OUT>21</STRUCK_OUT>

<HIT_BY_PITCH>0</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Williams</SURNAME>

<GIVEN_NAME>Gerald</GIVEN_NAME>

<POSITION>Outfield</POSITION>

<GAMES>129</GAMES>

<GAMES_STARTED>51</GAMES_STARTED>

<AT_BATS>266</AT_BATS>

<RUNS>46</RUNS>

<HITS>81</HITS>

<DOUBLES>18</DOUBLES>

<TRIPLES>3</TRIPLES>

<HOME_RUNS>10</HOME_RUNS>

<RBI>44</RBI>

<STEALS>1</STEALS>

<CAUGHT_STEALING>5</CAUGHT_STEALING>

<SACRIFICE_HITS>2</SACRIFICE_HITS>

<SACRIFICE_FLIES>1</SACRIFICE_FLIES>

<ERRORS>5</ERRORS>

<WALKS>17</WALKS>

<STRUCK_OUT>48</STRUCK_OUT>

<HIT_BY_PITCH>3</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Glavine</SURNAME>

<GIVEN_NAME>Tom</GIVEN_NAME>

<POSITION>Starting Pitcher</POSITION>

<WINS>20</WINS>

<LOSSES>6</LOSSES>

<SAVES>0</SAVES>

<GAMES>33</GAMES>

<GAMES_STARTED>33</GAMES_STARTED>

<COMPLETE_GAMES>4</COMPLETE_GAMES>

<SHUT_OUTS>3</SHUT_OUTS>

<ERA>2.47</ERA>

<INNINGS>229.1</INNINGS>

<HOME_RUNS>202</HOME_RUNS>

<RUNS>13</RUNS>

<EARNED_RUNS>67</EARNED_RUNS>

<HIT_BATTER>63</HIT_BATTER>

<WILD_PITCHES>2</WILD_PITCHES>

<BALK>3</BALK>

<WALKED_BATTER>0</WALKED_BATTER>

<STRUCK_OUT_BATTER>74</STRUCK_OUT_BATTER>

</PLAYER>

<PLAYER>

<SURNAME>Lopez</SURNAME>

<GIVEN_NAME>Javier</GIVEN_NAME>

<POSITION>Catcher</POSITION>

<GAMES>133</GAMES>

<GAMES_STARTED>124</GAMES_STARTED>

<AT_BATS>489</AT_BATS>

<RUNS>73</RUNS>

<HITS>139</HITS>

<DOUBLES>21</DOUBLES>

<TRIPLES>1</TRIPLES>

<HOME_RUNS>34</HOME_RUNS>

<RBI>106</RBI>

<STEALS>5</STEALS>

<CAUGHT_STEALING>3</CAUGHT_STEALING>

<SACRIFICE_HITS>1</SACRIFICE_HITS>

<SACRIFICE_FLIES>8</SACRIFICE_FLIES>

<ERRORS>5</ERRORS>

<WALKS>30</WALKS>

<STRUCK_OUT>85</STRUCK_OUT>

<HIT_BY_PITCH>6</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Klesko</SURNAME>

<GIVEN_NAME>Ryan</GIVEN_NAME>

<POSITION>Outfield</POSITION>

<GAMES>129</GAMES>

<GAMES_STARTED>124</GAMES_STARTED>

<AT_BATS>427</AT_BATS>

<RUNS>69</RUNS>

<HITS>17</HITS>

<DOUBLES>29</DOUBLES>

<TRIPLES>1</TRIPLES>

<HOME_RUNS>18</HOME_RUNS>

<RBI>70</RBI>

<STEALS>5</STEALS>

<CAUGHT_STEALING>3</CAUGHT_STEALING>

<SACRIFICE_HITS>0</SACRIFICE_HITS>

<SACRIFICE_FLIES>4</SACRIFICE_FLIES>

<ERRORS>2</ERRORS>

<WALKS>56</WALKS>

<STRUCK_OUT>66</STRUCK_OUT>

<HIT_BY_PITCH>3</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Galarraga</SURNAME>

<GIVEN_NAME>Andres</GIVEN_NAME>

<POSITION>First Base</POSITION>

<GAMES>153</GAMES>

<GAMES_STARTED>151</GAMES_STARTED>

<AT_BATS>555</AT_BATS>

<RUNS>103</RUNS>

<HITS>169</HITS>

<DOUBLES>27</DOUBLES>

<TRIPLES>1</TRIPLES>

<HOME_RUNS>44</HOME_RUNS>

<RBI>121</RBI>

<STEALS>7</STEALS>

<CAUGHT_STEALING>6</CAUGHT_STEALING>

<SACRIFICE_HITS>0</SACRIFICE_HITS>

<SACRIFICE_FLIES>5</SACRIFICE_FLIES>

<ERRORS>1</ERRORS>

<WALKS>63</WALKS>

<STRUCK_OUT>146</STRUCK_OUT>

<HIT_BY_PITCH>25</HIT_BY_PITCH>

</PLAYER>

<PLAYER>

<SURNAME>Helms</SURNAME>

<GIVEN_NAME>Wes</GIVEN_NAME>

<POSITION>Third Base</POSITION>

<GAMES>7</GAMES>

<GAMES_STARTED>2</GAMES_STARTED>

<AT_BATS>13</AT_BATS>

<RUNS>2</RUNS>

<HITS>4</HITS>

<DOUBLES>1</DOUBLES>

<TRIPLES>0</TRIPLES>

<HOME_RUNS>1</HOME_RUNS>

<RBI>2</RBI>

<STEALS>0</STEALS>

<CAUGHT_STEALING>0</CAUGHT_STEALING>

<SACRIFICE_HITS>0</SACRIFICE_HITS>

<SACRIFICE_FLIES>0</SACRIFICE_FLIES>

<ERRORS>1</ERRORS>

<WALKS>0</WALKS>

<STRUCK_OUT>4</STRUCK_OUT>

<HIT_BY_PITCH>0</HIT_BY_PITCH></PLAYER>

</TEAM>

<TEAM>

<TEAM_CITY>Florida</TEAM_CITY>

<TEAM_NAME>Marlins</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Montreal</TEAM_CITY>

<TEAM_NAME>Expos</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>New York</TEAM_CITY>

<TEAM_NAME>Mets</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Philadelphia</TEAM_CITY>

<TEAM_NAME>Phillies</TEAM_NAME>

</TEAM>

</DIVISION>

<DIVISION>

<DIVISION_NAME>Central</DIVISION_NAME>

<TEAM>

<TEAM_CITY>Chicago</TEAM_CITY>

<TEAM_NAME>Cubs</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Cincinatti</TEAM_CITY>

<TEAM_NAME>Reds</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Houston</TEAM_CITY>

<TEAM_NAME>Astros</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Milwaukee</TEAM_CITY>

<TEAM_NAME>Brewers</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Pittsburgh</TEAM_CITY>

<TEAM_NAME>Pirates</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>St. Louis</TEAM_CITY>

<TEAM_NAME>Cardinals</TEAM_NAME>

</TEAM>

</DIVISION>

<DIVISION>

<DIVISION_NAME>West</DIVISION_NAME>

<TEAM>

<TEAM_CITY>Arizona</TEAM_CITY>

<TEAM_NAME>Diamondbacks</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Colorado</TEAM_CITY>

<TEAM_NAME>Rockies</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Los Angeles</TEAM_CITY>

<TEAM_NAME>Dodgers</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>San Diego</TEAM_CITY>

<TEAM_NAME>Padres</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>San Francisco</TEAM_CITY>

<TEAM_NAME>Giants</TEAM_NAME>

</TEAM>

</DIVISION>

</LEAGUE>

<LEAGUE>

<LEAGUE_NAME>American League</LEAGUE_NAME>

<DIVISION>

<DIVISION_NAME>East</DIVISION_NAME>

<TEAM>

<TEAM_CITY>Baltimore</TEAM_CITY>

<TEAM_NAME>Orioles</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Boston</TEAM_CITY>

<TEAM_NAME>Red Sox</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>New York</TEAM_CITY>

<TEAM_NAME>Yankees</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Tampa Bay</TEAM_CITY>

<TEAM_NAME>Devil Rays</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Toronto</TEAM_CITY>

<TEAM_NAME>Blue Jays</TEAM_NAME>

</TEAM>

</DIVISION>

<DIVISION>

<DIVISION_NAME>Central</DIVISION_NAME>

<TEAM>

<TEAM_CITY>Chicago</TEAM_CITY>

<TEAM_NAME>White Sox</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Kansas City</TEAM_CITY>

<TEAM_NAME>Royals</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Detroit</TEAM_CITY>

<TEAM_NAME>Tigers</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Cleveland</TEAM_CITY>

<TEAM_NAME>Indians</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Minnesota</TEAM_CITY>

<TEAM_NAME>Twins</TEAM_NAME>

</TEAM>

</DIVISION>

<DIVISION>

<DIVISION_NAME>West</DIVISION_NAME>

<TEAM>

<TEAM_CITY>Anaheim</TEAM_CITY>

<TEAM_NAME>Angels</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Oakland</TEAM_CITY>

<TEAM_NAME>Athletics</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Seattle</TEAM_CITY>

<TEAM_NAME>Mariners</TEAM_NAME>

</TEAM>

<TEAM>

<TEAM_CITY>Texas</TEAM_CITY>

<TEAM_NAME>Rangers</TEAM_NAME>

</TEAM>

</DIVISION>

</LEAGUE>

</SEASON>

4-1显示的是将本文档装入Internet Explorer 5.0的情况。

4-1 Internet Explorer 5.0中显示的1998年主要联赛的统计数据

即使现在这个文档也是不完全的。此文档只包括一个队的球员(Atlanta Braves队)而且只有该球队的九个球员。如果将全部都写出来的话,则示例就会变得太长,以至于本书无法将其包括。

在名为1998statistics.xml的更为完整的XML文档中,包括了1998年度两大联赛的所有球员的统计数据,这个文档附在本书光盘中,目录为examples/base-ball。同时,我故意将所包括的数据加以限制,以便符合本书的篇幅。实际上,可以包括更为详细的数据。我已经间接提到可按比赛场次、投球次数等来安排数据的可能性。即使没有那样做,还是有许多细节可以添加到每个元素中。球队还有教练、经理、老板(说到Yankees队怎能不提到George Steinbrenner呢?)、室内运动场和其他项目。

我还故意忽略了可以从这里给出的其他项目中计算出来的数字,如平均击球数等。不管如何,球员还有许多其他数据,如臂长、身高、出生日期等。当然球员远不止这里列出的几个。所有这一切都是很容易加进XML文档的。但是我们的XML化就到此为止了,这样我们才能往下进行,首先要简短地讨论一下为什么这一数据格式是有用的,然后再讨论在Web浏览器上实际显示该文档所用的技术。

4.3 XML格式的优点

4-1对于显示一个球队的击球数据是简捷且易于理解的。我们从改写成的简单4-1中的长得多的形式中会得到什么好处呢?好处有如下几种:

* 数据是自说明的

* 数据可用标准工具加以处理

* 数据可用标准工具查看

* 用样式单可容易地生成同样数据的不同视图

XML格式的第一条主要好处是数据是自描述的。每个数字的意义是清楚的,且不会错误地与数字本身相联系。当读取文档时,用户了解<HITS> 2 </HITS>中的2指的是击中数而不是得分或是防守。如果键入文档的打字员漏掉了一个数字,不会造成其后的数字都错了位。HITS就是HITS,即使它前面的RUNS元素丢失也没关系。

在本书第二部分中,读者会看到,XML还可以使用DTD来加强限制,使得某些元素,如HITSRUNS必须存在。

第二条好处是XML提供的数据可用广泛的具有XML处理能力的工具加以处理,从相当贵的软件,如Adobe FrameMaker 到免费软件,如PythonPerl。数据量可以很大,但是数据额外的冗余就允许使用更多的工具来处理它。

当查看数据时,也同样有这样的问题。XML文档可装入Internet Explorer 5.0MozillaFrameMaker 5.5.6和许多其他工具,所有这些工具都提供唯一的、有用的一种数据的视图。数据还可以装入简单的文本编辑器中,如viBBEditTextPad。这就使得数据或多或少的可在多种平台上查看。

使用新软件也不是获得数据的不同视图的唯一方法。在下一节中,我们将为棒球统计数据创建一个样式单,来提供一种与图4-1完全不同的查看数据的方法。每当对同一文档施加不同的样式单,都可以看到不同图景。

最后,要向自己发问,文件大小真是很成问题吗?当前硬盘容量已经相当大了,可以存入大量数据,即使存储得不太节省也没有太大的关系。同时,XML文件的压缩率很大。全部的两大棒球联赛1998年统计数据的文档是653K。如果用gzip 压缩一下的话,只有66K,几乎压缩了90%。先进的HTTP服务器,如Jigsaw可以发送压缩文件,而不必解压缩,因而文档所用的网络带宽与其实际信息内容已相当接近。最后,我们不能认为二进制文件格式(特别通用的格式)必定是高效的。包含1998statistics.xml文件同样数据的Microsoft Excel文件的大小达到了2.37MB,比XML格式大了三倍多。虽然我们能够创建更为有效的文件格式和编码方法,但实际上简单并不是必须的。

4.4 编制样式单以便显示文档

4-1中的XML文档的原始视图对于某些应用来说也是不错的。例如,此视图允许折叠和展开单个的元素,因而可以只看文档中要看的部分。但大多数时候,人们总希望看到更好的形式,特别是,想要在Web上显示数据时。为了提供更好的外观,必须为文档编写样式单。

在本章中,我们使用的是CSS样式单。CSS样式单将特定的格式化信息与文档中的每个元素联系起来。我们的XML文档中使用的元素的完全列表如下:

 

SEASON

YEAR

LEAGUE

LEAGUE_NAME

DIVISION

DIVISION_NAME

TEAM

TEAM_CITY

TEAM_NAME

PLAYER

SURNAME

GIVEN_NAME

POSITION

GAMES

GAMES_STARTED

AT_BATS

RUNS

HITS

DOUBLES

TRIPLES

HOME_RUNS

RBI

STEALS

CAUGHT_STEALING

SACRIFICE_HITS

SACRIFICE_FLIES

ERRORS

WALKS

STRUCK_OUT

HIT_BY_PITCH

 

 

一般来说,我们要用重复的过程来为每个元素增加样式规则,一次一个元素地进行,然后检查是否达到了要求,再处理下一个元素。在本例中,这种办法对于不熟悉样式单属性的人来说也有好处。

4.4.1 与样式单连接

样式单的名称可随便取。如果只是为一个文档编制样式单,那么习惯上样式单的文件与文档的文件名一样,但是三字母的扩展名是.css而不是.xml。例如,对于XML文档1998shortstats.xml来说,样式单文件可以叫做1998shortstats.css。另一方面,如果同样的样式单还要用于许多文档,那么,可能需要更为普通的文件名,如baseballstats.css

由于CSS样式单是级联的,同一文档可有不止一个样式单。因而baseballstats.css可向文档施加某些一般的样式规则,而1998shortstats.css可覆盖其中的几条规则,以便在同一文档(1998shortstats.xml)中处理特定的细节。我们将第12"级联样式单(级别1"中讨论这一问题。

为了将样式单与文档联系起来,只要像下面所示简单地在XML声明和根元素间增加一个<?xml-stylesheet?>处理指令就可以了:

4.4.2 为根元素指定样式规则

用户不必为每个元素指定样式规则。许多元素允许将其父元素的样式串接下来。因而最重要的样式是根元素的样式,在本例中就是SEASON元素。这个样式定义了页面上所有其他元素的缺省样式。大致为72 dpi的分辨率的计算机显示器不如纸上300dpi或更大的分辨率那样高。所以,Web页面通常应该使用较大磅数的字号。首先将缺省样式定义为白色背景上的14磅黑色字,定义如下:

SEASON {font-size: 14pt; background-color: white;

color: black; display: block}

将这条语句放在一个文本文件中,将其以文件名baseballstats.css与清单4-1中的文件(1998shortstats.xml)保存在同一目录中。在浏览器中打开1998shortstats.xml。我们就会看到如图4-3所示的情况。

在图4-2和图4-3之间字号发生了变化,但文本颜色和背景颜色没有变化。其实这没有必要加以设置,因为黑色文本和白色背景是缺省的。但明确地加以设置也没有损失什么。

4-3 14磅白地黑字显示的棒球统计数据

4.4.3 为标题指定样式规则

元素YEAR或多或少可算是文档的标题。因而使其显示得大一些,用32磅的字号也就足够大了。同时,它还应该从文档的其余部分突出出来,而不是简单地与其他内容混在一起。利用下面的样式规则可以达到这些目的:

YEAR {display: block; font-size: 32pt; font-weight: bold;

text-align: center}

4-4显示的是将此规则增加到样式单中之后的文档。请特别注意,在"1998"后面的换行。有这个换行是由于YEAR是块级元素。而在文档中的其他元素都是内联元素。我们只能使块级元素居中(或左对齐、右对齐或两端对齐)。

4-4 YEAR元素格式化为标题

在使用了这种样式单的文档中,YEAR元素与HTML中的H1标题元素的功能重复了。 由于这个文档是非常整齐地分支结构,几个其他元素的功能与HTML中的H2H3等相似。这些元素都可以用相似的规则加以格式化,只是将字号略微减小一些罢了。

例如,SEASON由两个LEAGUE元素组成。每个LEAGUE的名称,即LEAGUE_NAME元素,起了HTML中的H2元素一样的作用。每个LEAGUE元素又由三个DIVISION元素所组成。每个DIVISION的名称,也就是DIVISION_NAME元素,具有HTML中的H3元素的作用。这两条规则分别将这两种元素加以格式化:

 

LEAGUE_NAME {display: block; text-align: center; font-size:

28pt; font-weight: bold}

DIVISION_NAME {display: block; text-align: center; font-size:

24pt; font-weight: bold}

 

 

4-5显示的是最后的文档。

4-5 LEAGUE_NAMEDIVISION_NAME元素格式化为下级标题

HTMLXML的一个重要区别是,在HTML中通常不会出现在一个元素中既包括节标题(H2H3H4等),又包括该节的完整内容的情况。节的内容必须包括在一级标题的结束和下一个同级标题的开始之间。这对于必须分析HTML文档的语法的软件来说是非常重要的,例如,要自动生成目录时。

Divisions又分成为TEAM元素。要将此格式化需要一些技巧,因为球队的标题并不就是TEAM_NAME元素,而是TEAM_CITY元素与TEAM_NAME拼接在一起的。所以这需要的是内联元素而不是单独的块级元素。然而,它们仍然是标题,因而我们将其设置为粗斜体的20磅字体。图4-6显示的是将这两条规则加到样式单中的结果。

 

TEAM_CITY {font-size: 20pt; font-weight: bold;

font-style: italic}

TEAM_NAME {font-size: 20pt; font-weight: bold;

font-style: italic}

 

 

4-6 为队名设置样式

到此为止,将队名与城市名作为结合起来的块级元素来排列结果可能会是不错的。有几种办法可达到这个目的。例如,可以向XML文档中增加一个附加的TEAM_TITLE元素,其目的只是为了包括TEAM_NAMETEAM_CITY。例如:

 

 

 

Colorado

Rockies

 

 

 

 

接着,可以增加一条向TEAM_TITLE施加块级格式化的样式规则:

TEAM_TITLE {display: block; text-align: center}

但是,绝不应该为了使样式单简单一些而重新排列XML文档。毕竟,样式单的总的目的是将格式化信息保存于文档之外。不过,用户可以通过别的办法达到同样的效果。其办法是,使紧挨着的上一个和下一个元素变成块级元素,也就是说,将TEAMPLAYER变成块级元素。这就将TEAM_NAMETEAM_CITY放在了由它们本身组成的隐式块级元素之中了。图4-7显示了其结果。

TEAM {display: block}

PLAYER {display: block}

4-7 作为段标题而格式化的队名和城市名

4.4.4 为球员和统计元素指定样式规则

本文档需要的最具技巧的格式化是对每个球员及其统计数据的格式化。每个队有几十个球员。每个球员都有统计数据。应该将TEAM元素看作是由PLAYER元素组成的,且将每个球员放在他自己的块级节中,正如前一个元素所做的那样。不过,排列这些数据的更为吸引人且更为有效的方法是使用表格。达到这一目的的样式规则如下所示:

 

TEAM {display: table}

TEAM_CITY {display: table-caption}

TEAM_NAME {display: table-caption}

PLAYER {display: table-row}

SURNAME {display: table-cell}

GIVEN_NAME {display: table-cell}

POSITION {display: table-cell}

GAMES {display: table-cell}

GAMES_STARTED {display: table-cell}

AT_BATS {display: table-cell}

RUNS {display: table-cell}

HITS {display: table-cell}

DOUBLES {display: table-cell}

TRIPLES {display: table-cell}

HOME_RUNS {display: table-cell}

RBI {display: table-cell}

STEALS {display: table-cell}

CAUGHT_STEALING {display: table-cell}

SACRIFICE_HITS {display: table-cell}

SACRIFICE_FLIES {display: table-cell}

ERRORS {display: table-cell}

WALKS {display: table-cell}

STRUCK_OUT {display: table-cell}

HIT_BY_PITCH {display: table-cell}

 

 

遗憾的是,只有CSS2才支持表格属性,而Internet Explorer 5.0和其他写作本书时已存在的浏览器还不支持CSS2。由于还不能使用表格的格式化方法,我们只好使TEAMPLAYER成为块级元素,而让其他数据保持缺省格式。

4.4.5 本节小结

清单4-2列出了完成后的样式单。CSS样式单除了一条一条的规则之外,这种样式单没有什么结构。实际上,样式单只是我在上面分别介绍过的所有规则的列表。列表中的顺序不是很重要,只要每条规则都包含进去也就可以了。

清单4-2

baseballstats.css

SEASON {font-size: 4pt; background-color: white;

color: black; display: block}

YEAR {display: block; font-size: 32pt; font-weight: bold;

text-align: center}

LEAGUE_NAME {display: block; text-align: center;

font-size: 28pt; font-weight: bold}

DIVISION_NAME {display: block; text-align: center;

font-size: 24pt; font-weight: bold}

TEAM_CITY {font-size: 20pt; font-weight: bold;

font-style: italic}

TEAM_NAME {font-size: 20pt; font-weight: bold;

font-style: italic}

TEAM {display: block}

PLAYER {display: block}

 

到此就完成了棒球统计数据的基本格式化的任务。不过很清楚,还有许多工作要做。支持真正表格格式化的浏览器将会大有帮助。然而还有其他工作。下面指出这些工作,其顺序没有什么关系:

* 只是列出了原始的数字,而没有说明数字代表了什么。每个数字应该有一个为其命名的标题,如“RBI"或是"At Bats"

* 令人感兴趣的数据,如平均击球数由于是可能从这里列出的数据中计算出来的,这样的数据就没有包括进来。

* 某些标题有点太短。如果文档的标题是“1998 Major League Baseball"而不是简单的"1998"可能会更好。

* 如果Major League中的所有球员都包括进来,这一文档就会如此之长,以至于难以阅读。在这种情况下,与Internet Explorer中的可折叠的大纲视图类似的东西可能会更有用。

* 由于投手统计数据与击球手的数据是如此不同,在花名册中分别排序可能会更好。

像这样一类的许多看法都应该向文档中增加更多的内容加以体现。例如,为了将标题从“1998"改为"1998 Major League Baseball",所要做的工作只是将YEAR 元素改写如下:

1998 Major League Baseball

在每个花名册的顶部,用一个假想的球员名,为球员的统计数据加进小标题,如下所示:

 

 

Surname

Given name

Position

Games

Games Started

At Bats

Runs

Hits

Doubles

Triples

Home Runs

Runs Batted In

Steals

Caught Stealing

Sacrifice Hits

Sacrifice Flies

Errors

Walks

Struck Out

Hit By Pitch

 

 

 

关于这种方法还有一些基本问题需要解决。年份是1998年,而不是1998 Major League Baseball 。小标题"At Bats"与击球数不是一回事。(这正是事物的名称与事物本身之间的差别。)这时可增加一些标记如下(加以解决):

 

 

Surname

Given name

Position

Games

Games Started

At Bats

Runs

Hits

Doubles

Triples

Home Runs

Runs Batted In

Steals

Caught Stealing

Sacrifice Hits

Sacrifice Flies

Errors

Walks

Struck Out

Hit By Pitch

 

 

 

不过这样一来,基本上是重新发明HTML,而且使我们又回到了使用标记来格式化而不是用于意义了。同时,我们还重复了已经包括在元素名称中的信息。整个文档还相当大,我们还是希望文档不要太大为好。

增加击球和其他的平均数并不复杂。只要将数据作为附加的元素包括进来就可以了。例如,下面是一个带有该种数据的球员:

 

 

Malloy

Marty

Second Base

1

8

.233

.321

.179

28

3

5

1

0

1

1

0

0

0

0

0

2

2

0

 

 

 

但是,这种信息是多余的,因为这些数据可从已经包括进来的数据中计算出来。例如,平均击球数是击中的垒数被击球数除的结果,也就是HITS/AT_BAT。多余数据使得维护和更新数据变得非常困难。对一个元素的简单的改变或是增加都会引起多个位置的改变和重新计算。

真正所需要的是一种不同的样式单语言,能使我们向元素中增加样板内容并根据现存的元素内容执行转换。这样的语言是存在的,这就是可扩展的样式语言(Extensible Style Language,简写为XSL)。

可扩展的样式语言(Extensible Style LanguageXSL)将在第1415章中加以讨论。

CSSXSL简单,对于基本的Web页面来说,也更适合一些,而且也是更为直接的文档。XSL变得相当复杂,但功能也更为强大。XSL是建立在我们已经在上面学到的简单的CSS格式化的基础之上的,但是也提供了将源文档转换为读者可以查看的不同形式的方法。在调试XML时,首先使用CSS寻找问题,然后再转到XSL,以便获得更大的灵活性,这通常是不错的主意。

4.5 本章小结

在本章中,读者看到了几个展示如何从头创建XML文档的示例。我们特别学到了如下内容:

* 如何检查包括在XML文件中的数据,以便标识元素。

* 如何用自己定义的XML标记来标记数据。

* XML格式所提供的比传统格式的优越性。

* 如何编写样式单,使文档格式化并显示出来。

本章中充满了枯燥的代码。文档是在没有太多的细节的情况下编写出来的。在下一章中,我们将要探讨在XML文档中嵌入信息的附加意义,包括特性、注释和处理指令,并看一看在XML中用另一种对棒球统计数据编码的方法。