|
第4章 数据的结构化在本章中,我们将要研究一个较长的示例,用来说明一个较长的有关棒球统计和其他类似数据的列表是如何以XML格式保存的。像这样的文档有好多潜在的应用。最明显的,它可以显示在Web 页面上。还可以用作其他分析数据或是整理数据程序的输入。通过这个示例,读者将学到如何用XML来标记数据、为什么要选用XML标记、如何为文档编制CSS样式单等等内容。 本章的主要内容包括: · 检查数据
4.1 检查数据当我写作这本书时(1998年10月),纽约的Yankees队在四场比赛中击败圣·迭格的Padres队,取得了24届世界系列赛的冠军。Yankees队在American League的普通赛季结束时,取得了114场胜利。总体来说,1998是一个令人赞叹的赛季。圣·路易斯Cardinals队的 Mark McGwire和芝加哥Cubs队的Sammy Sosa为了创造新的单一赛季的本垒打纪录在整个9月份展开了争夺,原来的纪录是由Roger Maris保持的。 是什么使1998赛季这样激动人心呢?玩世不恭的人会告诉你,1998是一扩展年,有三个新队加盟,因而总体上来说投手能力减弱了。这就使得著名的击球手如Sosa和McGwire以及著名的球队,如Yankees得到了出风头的机会,因为,虽然他们仍然像他们在1997年一样实力强大,但面对的对手的平均能力弱了许多。当然真正的棒球爱好者了解真正的原因,这是由于统计上的原因造成的。 这实在有点滑稽。在大多数体育项目中,我们都说过心脏、勇气、能力、技巧、决心和其他名词。但是,只有棒球爱好者需要面对这么多原始数字,如平均击球率、平均得分、平均跑垒数、平均进垒数、对左手投手的平均击球率、对右手投手的平均击球率等。 棒球爱好者都被这些数字所迷住了,数字越多越好。在每个赛季中,因特网成了成千上万的棒球爱好者的大本营,狂热的网民们在其中“管理”球队并交换球员,计算他们喜爱的球队在现实中表现的各种数字。STATS, Inc.公司跟踪了每个球员在主要联赛的赛事上的表现,因而可以计算出一个击球手是否表现得比他的平均成绩要好。在以下两节中,为了照顾对棒球不太感兴趣的读者,我们检查一下描述单个球员的击球和投球率的常用统计数字。现场统计数字也可以找到,但是我将把这些数字略去,以便将示例局限于好管理的大小。我使用的这个特殊的例子是纽约的Yankees队,对于任何队的击球手,同样的统计数字也可以得到。 4.1.1 击球手几年前,Bruce Bukiet、Jose Palacios和我写过一篇名为A Markov Chain Approach to Baseball (用于棒球的马尔可夫链式方法)的文章(刊登在Operations Research(运筹学研究杂志),45卷第1期,1997年1-2月号,pp. 14-23, 还可在以下网址上看到这篇文章http://www.math.njit.edu/~bukiet/Papers /ball.pdf)。在这篇文章中,我们分析了1989年全国棒球联赛中的所有球队的所有可能的比赛顺序。那篇文章的结果还是比较有意思的。球队中的最坏的击球手(通常是投球手)应该是第8位出场击球的人,而不应该是第9位,至少在全国棒球联赛上是如此。但是这里我所关心的是产生那篇文章的工作。作为一个低年级的研究生,用手工算出每个球员在全国棒球联赛上的全部击球历史记录正是我的工作。如果我能够使那些数据变得像XML一样的方便,那个夏季我会过得更愉快一些的。现在,让我们将精力集中于每个球员的数据上。典型的,这种数据是以一行行的数字表示的,如表4-1所示的是1998年Yankees队的进攻队员的数据。在美国棒球联赛的比赛上,由于投球手很少击球,只有实际上击球的队员才列在表中。 每一列有效地定义了一个元素。因而就需要为球员、位置、进行的比赛、击球、跑垒 、击球数、两垒、三垒、本垒打、跑入和步行等建立元素。单垒通常都不单独报告。这个数据是从总击打数中减去双垒、三垒和本垒打的总和后得到的。 表4-1 The 1998年Yankees队的进攻队员数据
译者注:棒球数据不过是一种演示。在棒球统计数据的XML文档中,由于使用的是英文专用名词,故这里未翻译成中文。如果翻译过来反而无法相互对照。表4-2也同样处理。 前面表中的数据和下一节中的投球手数据都是加以限制后的列表,只是用来表明在一个典型的棒球赛中收集的数据。除了列出的以外,还有许多其他数据没有在这里列出。我打算使用这些基本信息,以便使示例容易管理。 4.1.2 投球手人们并不指望投球手成为全垒跑的击球手或是偷袭能手。确实偶尔到达第一垒的投球手是对一个队的意外奖励。对投球手的评价要根据表4-2中列出的全场的不同种类的数字。这个表的每列也定义了一个元素。这些元素中的一部分,如姓名和位置对于投球手和击球手都是有的。其他元素如解救(saves)和成功防守(shutouts)只适用于投球手。还有几个,如得分(runs)和全垒跑(home runs)与击球手统计中的名称相同,但是具有不同意义。例如,击球手的得分数是击球手获得的分数。而对于投球手来说,是指对方在这个投球手下得到的分数。 4.1.3 XML数据的组织XML是建立在容器模型的基础之上的。每个XML元素可以包含文本或是称为子元素的其他XML元素。有几个XML元素既可以包含文本也可以包含子元素。虽然通常来说,这并不是一种好形式,是应该尽量避免的。 不过,常常有不止一种组织数据的方法,这要取决于需要。XML的一个好处是,它使得编写程序来以不同形式组织数据变得相当直接。在第14章我们讨论XSL变换时还要讨论这一问题。 作为开始,必须注意的第一个问题是什么包含什么?例如,相当明显的是,联赛包含分部,分部包含球队,球队又包含球员,而球员又可在指定的时间进行交易,每个球员必定属于一个球队,每个球队又必定属于一个分部。类似的,一个赛季包含许多场比赛,每场比赛又包含几局,而局又包含击球阶段,击球阶段又包含投球阶段。 但是,赛季包括联赛吗或是联赛包括赛季吗?这个问题就不是很明显。确实对这样的问题没有唯一的答案。将赛季元素定义为联赛元素的子元素还是将联赛元素变为赛季元素的子元素有更多的意义,这要依赖于数据要用来干什么。用户甚至可以创建新的既包含赛季也包含联赛的根元素,哪个元素也不是另外元素的子元素(虽然要有效地这样做,还需要某些先进的技术,在以下几章还讨论不到这些技术)。用户可按用户的意愿来组织数据。 表4-2 1998年Yankees队的投球手
4.2 数据的XML化让我们用XML处理1998年的Major League赛季数据的标记开始。请记住,在XML内,允许我们创建标记。我们已经决定,文档的根元素是赛季(season)。赛季包括联赛(leagues),而联赛包括分部(divisions),分部又包括球队(teams),球队包括队员(players)。队员的统计数字包括参加的场数(games played)、击球次数(at bats)、得分数(runs)、击中数(hits)、双垒(doubles)、三垒(triples)、全垒得分(home runs)、击球得分(runs batted in)、走步数(walks)和被投手击中数(hits by pitch)。 4.2.1 开始编写文档:XML声明和根元素XML文档可由XML声明加以识别。这是放在所有XML文档的开头的一条处理指令,标识正在使用的XML版本。当前可理解的唯一版本号是1.0。 <?xml version="1.0"?> 每个合格的XML文档(所谓合格有特定的意义,这将在下一章中加以讨论)必须有一个根元素。这是一个完全包括文档中其他所有元素的元素。根元素的起始标记要放在所有其他元素的起始标记之前,而根元素的结束标记要放在所有其他元素的结束标记之后。对于我们的根元素SEASON,其起始标记是<SEASON>,而结束标记是</SEASON>。文档现在看起来像下面的样子: <?xml version="1.0"?> <SEASON> </SEASON> XML声明既不是元素也不是标记。它是处理指令。因而不需要将声明放在根元素SEASON之内。但是,我们在文档中放入的每个元素都得放在起始标记<SEASON>和结束标记</SEASON>之间。 根元素的这种选择方法说明我们已经不能在一个文件中保存多个赛季的数据了。如果想要保存多个赛季的数据的话,可以定义一个新的包括赛季(seasons)的根元素,例如, <?xml version="1.0"?> <DOCUMENT> <SEASON> </SEASON> <SEASON> </SEASON> </DOCUMENT> 命名约定 在开始之前,我还要说几句关于命名约定的话。正如我们在下一章中所见到的,XML的元素名是比较灵活的,可以包括任意数目的字母和数字,既可是大写的也可是小写的。可以将XML标记写成下面的任何样子: <SEASON> <Season> <season> <season1998> <Season98> <season_98> 这就会有成千上万种可能的变化。全使用大写、全使用小写或是混合大小写都是可以的。但是,我推荐使用一种约定,并坚持下去。 当然,我们对所谈到的赛季加以标识。为达此目的,可为SEASON元素定义一个名为YEAR的子元素。例如: <?xml version="1.0"?> <SEASON> <YEAR> 1998 </YEAR> </SEASON> 我在此处以及其他例子中使用了缩进,以便指明元素YEAR是元素SEASON的子元素,而文本1998是元素YEAR的内容。这是一种很好的编程习惯,但这不是必须的。XML中的空白没有特殊的意义。同样的例子也可写成下面的样子: <?xml version="1.0"?> <SEASON> <YEAR>1998</YEAR> </SEASON> 确实,我经常将元素压缩到一行上(当一行上可以放得下,而空间又比较紧张时)。还可以将文档再加以压缩,即使压缩成一行也可以,但这要失去可读性。例如: <?xml version="1.0"?><SEASON><YEAR>1998</YEAR></SEASON> 当然这样的文档是比较难以阅读和理解的,这也就是为什么我没有这样书写的原因。XML 1.0规范中的第十条目的中写道:“Terseness in XML markup is of minimal importance.”翻译成中文是,“XML标记中的简捷性是不太重要的。”棒球示例完全反映出了这个目的。 4.2.2 联赛(League)、(分部)Division和(球队)Team数据的XML化主要棒球联赛分成两个联赛:American League和National League。每个联赛都有名称。两个名称可如下编码: <?xml version="1.0"?> <SEASON> <YEAR>1998</YEAR> <LEAGUE> <LEAGUE_NAME>National League</LEAGUE_NAME> </LEAGUE> <LEAGUE> <LEAGUE_NAME>American League</LEAGUE_NAME> </LEAGUE> </SEASON> 我在这里将联赛的名称定义为元素LEAGUE_NAME,而不是简单的NAME元素。因为NAME太普遍了,而且还打算将其用在其他场合。例如,分部、球队和球员都有名称。 带有相同的名称的不同领域的元素可以利用命名域(namespaces)结合在一起。命名域的问题将在第18章中加以讨论。但是,即使使用命名域,也不要将同一领域(如本例中的TEAM和LEAGUE)的多个术语给予同样的名称。 每个联赛可分为东部(east)、西部(west)和中部(central)分部,可编码如下: <LEAGUE> <LEAGUE_NAME>National League</LEAGUE_NAME> <DIVISION> <DIVISION_NAME>East</DIVISION_NAME> </DIVISION> <DIVISION> <DIVISION_NAME>Central</DIVISION_NAME> </DIVISION> <DIVISION> <DIVISION_NAME>West</DIVISION_NAME> </DIVISION> </LEAGUE> <LEAGUE> <LEAGUE_NAME>American League</LEAGUE_NAME> <DIVISION> <DIVISION_NAME>East</DIVISION_NAME> </DIVISION> <DIVISION> <DIVISION_NAME>Central</DIVISION_NAME> </DIVISION> <DIVISION> <DIVISION_NAME>West</DIVISION_NAME> </DIVISION> </LEAGUE> 元素的实际值依赖于包括该元素的父元素。American League和National League都有East分部,但是这不是一回事。 每个分部又分为多个球队。每个球队都有一个队名和城市名。例如,与American League联赛East分部有关的名称可编码如下: <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> 4.2.3 球员数据的XML化每个球队是由球员组成的。每个球员都有姓和名。将姓和名分开是重要的,这样一来既可以根据名来分类也可以根据姓来分类。1998年Yankees阵容第一个出场的投球手的数据可编码如下: <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 Girardi在1998年的统计数据。他是一个接球手,因而我们使用击球的统计数据: <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文档。但现在是该将各段组装在一起,看一看包括1998年Major 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来加强限制,使得某些元素,如HITS或RUNS必须存在。 第二条好处是XML提供的数据可用广泛的具有XML处理能力的工具加以处理,从相当贵的软件,如Adobe FrameMaker 到免费软件,如Python和Perl。数据量可以很大,但是数据额外的冗余就允许使用更多的工具来处理它。 当查看数据时,也同样有这样的问题。XML文档可装入Internet Explorer 5.0、Mozilla、FrameMaker 5.5.6和许多其他工具,所有这些工具都提供唯一的、有用的一种数据的视图。数据还可以装入简单的文本编辑器中,如vi、BBEdit和TextPad。这就使得数据或多或少的可在多种平台上查看。 使用新软件也不是获得数据的不同视图的唯一方法。在下一节中,我们将为棒球统计数据创建一个样式单,来提供一种与图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?>处理指令就可以了: <?xml version="1.0" standalone="yes"?> <?xml-stylesheet type="text/css" href="baseballstats.css"?> <SEASON> ... 这条指令告诉浏览器读取文档并施加保存在文件baseballstats.css中的样式单。这个文件是假设放在与XML文件同一服务器上的同一目录中的。换句话说,baseballstats.css是个相对的URL。完全的URL也是可以使用的。例如: <?xml version="1.0" standalone="yes"?> <?xml-stylesheet type="text/css" href="http://metalab.unc.edu/xml/examples/baseballstats.css"?> <SEASON> ... 开始时,用户可以简单地将一个名为baseballstats.css的空文件放在与XML文档相同的目录中。然后向1998shortstats.xml (清单4-1)中增加适当的指令,该文档现在在浏览器中的外观如图4-2所示。只显示了元素内容。可折叠的大纲视图(图4-1)不见了。元素内容的格式使用的是浏览器的缺省格式,在本例中是黑色12磅的Times Roman 字体放在白色背景上。 图4-2 使用了空白样式之后的1998年两大棒球联赛的统计数字显示 如果在指定位置找不到样式单处理指令(xml-stylesheet)中指定的样式单文件名,也可看到一个很像图4-2的视图。 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中的H2、H3等相似。这些元素都可以用相似的规则加以格式化,只是将字号略微减小一些罢了。 例如,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_NAME和DIVISION_NAME元素格式化为下级标题 HTML和XML的一个重要区别是,在HTML中通常不会出现在一个元素中既包括节标题(H2、H3、H4等),又包括该节的完整内容的情况。节的内容必须包括在一级标题的结束和下一个同级标题的开始之间。这对于必须分析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_NAME和TEAM_CITY。例如: <TEAM> <TEAM_TITLE> <TEAM_CITY>Colorado</TEAM_CITY> <TEAM_NAME>Rockies</TEAM_NAME> </TEAM_TITLE> </TEAM> 接着,可以增加一条向TEAM_TITLE施加块级格式化的样式规则: TEAM_TITLE {display: block; text-align: center} 但是,绝不应该为了使样式单简单一些而重新排列XML文档。毕竟,样式单的总的目的是将格式化信息保存于文档之外。不过,用户可以通过别的办法达到同样的效果。其办法是,使紧挨着的上一个和下一个元素变成块级元素,也就是说,将TEAM和PLAYER变成块级元素。这就将TEAM_NAME和TEAM_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。由于还不能使用表格的格式化方法,我们只好使TEAM和PLAYER成为块级元素,而让其他数据保持缺省格式。 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”改为“1998 Major League Baseball”,所要做的工作只是将YEAR 元素改写如下: 1998 Major League Baseball 在每个花名册的顶部,用一个假想的球员名,为球员的统计数据加进小标题,如下所示: <PLAYER> <SURNAME>Surname</SURNAME> <GIVEN_NAME>Given name</GIVEN_NAME> <POSITION>Position</POSITION> <GAMES>Games</GAMES> <GAMES_STARTED>Games Started</GAMES_STARTED> <AT_BATS>At Bats</AT_BATS> <RUNS>Runs</RUNS> <HITS>Hits</HITS> <DOUBLES>Doubles</DOUBLES> <TRIPLES>Triples</TRIPLES> <HOME_RUNS>Home Runs</HOME_RUNS> <RBI>Runs Batted In</RBI> <STEALS>Steals</STEALS> <CAUGHT_STEALING>Caught Stealing</CAUGHT_STEALING> <SACRIFICE_HITS>Sacrifice Hits</SACRIFICE_HITS> <SACRIFICE_FLIES>Sacrifice Flies</SACRIFICE_FLIES> <ERRORS>Errors</ERRORS> <WALKS>Walks</WALKS> <STRUCK_OUT>Struck Out</STRUCK_OUT> <HIT_BY_PITCH>Hit By Pitch</HIT_BY_PITCH> </PLAYER> 关于这种方法还有一些基本问题需要解决。年份是1998年,而不是1998 Major League Baseball 。小标题“At Bats”与击球数不是一回事。(这正是事物的名称与事物本身之间的差别。)这时可增加一些标记如下(加以解决): <TABLE_HEAD> <COLUMN_LABEL>Surname</COLUMN_LABEL> <COLUMN_LABEL>Given name</COLUMN_LABEL> <COLUMN_LABEL>Position</COLUMN_LABEL> <COLUMN_LABEL>Games</COLUMN_LABEL> <COLUMN_LABEL>Games Started</COLUMN_LABEL> <COLUMN_LABEL>At Bats</COLUMN_LABEL> <COLUMN_LABEL>Runs</COLUMN_LABEL> <COLUMN_LABEL>Hits</COLUMN_LABEL> <COLUMN_LABEL>Doubles</COLUMN_LABEL> <COLUMN_LABEL>Triples</COLUMN_LABEL> <COLUMN_LABEL>Home Runs</COLUMN_LABEL> <COLUMN_LABEL>Runs Batted In</COLUMN_LABEL> <COLUMN_LABEL>Steals</COLUMN_LABEL> <COLUMN_LABEL>Caught Stealing</COLUMN_LABEL> <COLUMN_LABEL>Sacrifice Hits</COLUMN_LABEL> <COLUMN_LABEL>Sacrifice Flies</COLUMN_LABEL> <COLUMN_LABEL>Errors</COLUMN_LABEL> <COLUMN_LABEL>Walks</COLUMN_LABEL> <COLUMN_LABEL>Struck Out</COLUMN_LABEL> <COLUMN_LABEL>Hit By Pitch</COLUMN_LABEL> </TABLE_HEAD> 不过这样一来,基本上是重新“发明”了HTML,而且使我们又回到了使用标记来格式化而不是用于意义了。同时,我们还重复了已经包括在元素名称中的信息。整个文档还相当大,我们还是希望文档不要太大为好。 增加击球和其他的平均数并不复杂。只要将数据作为附加的元素包括进来就可以了。例如,下面是一个带有该种数据的球员: <PLAYER> <SURNAME>Malloy</SURNAME> <GIVEN_NAME>Marty</GIVEN_NAME> <POSITION>Second Base</POSITION> <GAMES>1</GAMES> <GAMES_STARTED>8</GAMES_STARTED> <ON_BASE_AVERAGE>.233</ON_BASE_AVERAGE> <SLUGGING_AVERAGE>.321</SLUGGING_AVERAGE> <BATTING_AVERAGE>.179</BATTING_AVERAGE> <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> 但是,这种信息是多余的,因为这些数据可从已经包括进来的数据中计算出来。例如,平均击球数是击中的垒数被击球数除的结果,也就是HITS/AT_BAT。多余数据使得维护和更新数据变得非常困难。对一个元素的简单的改变或是增加都会引起多个位置的改变和重新计算。 真正所需要的是一种不同的样式单语言,能使我们向元素中增加样板内容并根据现存的元素内容执行转换。这样的语言是存在的,这就是可扩展的样式语言(Extensible Style Language,简写为XSL)。 可扩展的样式语言(Extensible Style Language,XSL)将在第14、15章中加以讨论。 CSS比XSL简单,对于基本的Web页面来说,也更适合一些,而且也是更为直接的文档。XSL变得相当复杂,但功能也更为强大。XSL是建立在我们已经在上面学到的简单的CSS格式化的基础之上的,但是也提供了将源文档转换为读者可以查看的不同形式的方法。在调试XML时,首先使用CSS寻找问题,然后再转到XSL,以便获得更大的灵活性,这通常是不错的主意。 4.5 本章小结在本章中,读者看到了几个展示如何从头创建XML文档的示例。我们特别学到了如下内容: · 如何检查包括在XML文件中的数据,以便标识元素。
本章中充满了枯燥的代码。文档是在没有太多的细节的情况下编写出来的。在下一章中,我们将要探讨在XML文档中嵌入信息的附加意义,包括特性、注释和处理指令,并看一看在XML中用另一种对棒球统计数据编码的方法。 |