设计XML数据结构

本节将讲述一些有启发性的东西,以帮助你决定XML设计。

减少你的工作量

只要可能,就应该使用已有的模式定义。忽略那些你无需关心的事情,总比自己从头搞起要容易得多。另外,应该使用标准的DTD来实现数据交换,并且可以使用他人开发的数据识别工具。

所以,如果已经有一个工业标准,那么就应该考虑使用一个外部参数实体来引用那个DTD。有一个地方可以找到工业标准的DTD,那就是由结构化信息标准促进组织(OASIS)创建的知识库,网址为http://www.XML.org。另一个这样的地方就是CommerceOne的XML Exchange,网址为http://www.xmlx.com,被描述为“a repository for creating and sharing document type definitions(一个关于创建和共享文档类型定义的知识库)”。


注意:在OASIS的网页http://www.oasis-open.org/cover/elementsAndAttrs.html上,还有更多的关于设计XML结构的思想。


属性和元素

在设计XML结构时经常碰到的一个问题就是:是将一个给定的数据项作为一个已有元素的子元素,还是作为该元素的一个属性。例如,你可以将一个幻灯片的标题做成这样:

<slide>
  <title>This is the title</title>
</slide> 

也可以是这样:

<slide title="This is the title">...</slide> 

在有些情况下,属性和元素因为具有不同的特征,因而很容易作出选择。让我们首先考虑一下这样的例子, 然后再将视线转移到那些选择起来比较困难的情况。

被迫的选择

有时候,由于某些属性和元素的本性,两者之间的选择其实是早已决定了的。让我们来看一看一些这样的情况:

含有子结构的数据

在这种情况下,数据项必须作为元素。它不能作为一个属性,因为属性只能带一个字符串。所以,如果标题可以含有强调的文本,如The <em>Best</em> Choice,则该标题必须是一个元素。

含有多行的数据

这这种情况下,使用元素还是更有用。属性都要求是简单、短小的字符串或其他东西,这使得它们往往可读性很差,如果可用的话。

存在多次出现的可能性

有些项会出现多次,例如文章中的图像,那么它就必须作为一个元素。含有这种项的元素只能带一个特定类型的属性,但是却可以有很多相同类型的子元素。

数据更改频繁

如果数据常常要经过一个编辑器来修改,那么这时将数据作为元素会更管用一些。很多能识别XML的编辑器都可以方便的修改数据,而要修改属性就没那么容易了。

数据是短小的、简单的字符串,改动不大

这种数据就可以作为属性。不过,你可以这么做,并不意味着你必须得这么做。不信的话,看看下一节“样式选择”。

如果数据只有少量固定的选择时,使用DTD

这种情况下,使用属性的确是可取的。DTD可以防止属性带有任何不在指定之列的值,但是DTD不能够类似地限制元素。(不过,如果使用模式的话,就可以既限制属性,又限制元素。)

风格选择

往往,我们面临的选择不像前面提到的情况那样局限。如果选择起来不是那样明确的话,你需要有“风格”意识来指导你的思想。这样说来,要回答的问题就是,什么可以产生良好的XML风格,为什么?

不幸的是,对于XML风格的定义,就像对于艺术或者音乐的风格定义那样的含糊。不过,还是有一些方法可以做到这一点的。本节的目的就是要给你一些关于“XML风格”这一主题的思想。

可见性

关于XML属性和元素的思考的一个启发就是,使用可见性的概念。如果数据需要显示(显示给一些终端用户),那么它就应该被作为一个元素。另一方面,如果信息是用于指导XML处理的,用户无法看到,那么这种数据最好应该作为一个属性。例如,对

于一双鞋子的订购条目,鞋子的码数应该定义成一个元素,而出厂号应该作为应该属性。

消费者/提供者

另一个关于可见性启发的思考就是,问一问自己谁是信息的消费者和/或提供者。鞋子的码数是由卖鞋的销售员输入的,所以它是一个元素。另一方面,对于一种给定型号的鞋子出厂号,则必须捆绑在应用中,或者存放在一个数据库中,所以它应该是一个属性。(不过,如果它是由职员输入的,则很可能是一个元素。)

容器 vs. 内容

或许关于元素和属性最好的思考方式就是将元素想象成一个容器。类比地,容器中的内容(水或者牛奶)则对应于XML中作为元素的数据。这种数据实质上就是变量。另一方面,容器的特性(蓝色或白色的大水罐)则可以看作是属性。这种信息往往倾向于稳定,变化少。从某种意义上来说,好的XML风格就是每个容器中的内容与容器的特征是分开的。

为了展示这些启发的实际作用,我们来看看一个幻灯片显示的例子,幻灯片的类型(执行性的或者技术性的)最好应该作为一个属性。正是幻灯片的类型决定了这种幻灯片是为一个特定的观众所接受还是抛弃。另一方面,幻灯片的标题,是该幻灯片内容的一部分。可见性的启发在此得到了应用。当放映幻灯片时,幻灯片的标题将被显示出来,但是它的内容不会显示出来。最后,在这个例中,标题信息的消费者是在场的观众,而类型信息的消费者则是当时运行的程序。

规格化数据

 “设计XML数据结构”一节已经展示了如何创建可以在XML文档中引用的外部实体。这种实体具有模块化例程的所有优点——对一个实体拷贝的更改就影响到所有引用了该实体的文档。消除冗余的处理过程就叫做规格化,所以定义实体是一种规格化数据的好方法。

在HTML文件中,要获得那种模块性的惟一方法就是使用HTML链接——不过,这样一来文档就支离破碎了,而不是作为一个整体。实体引用表现为宏观性——实体的内容逐渐膨胀,形成一个完整的文档,而不是一个文档片段。如果实体被定义在一个外部文件中,很多其他的文档都可以引用它。

关于定义实体引用的思想,与你在模块化编程代码中应用到的思想很类似:

  • 只要你发现自己正在不止一次地编写同样的东西,就应该想到实体。这样,你就可以只在一个地方编写一次实体,而在多个地方引用它。

  • 如果信息很可能需要更改,尤其是当信息被多个地方使用时,也应该想到定义一个实体。一个例子就是将productName定义成一个实体,以便当产品名称发生改变时,你可以方便地改变文档。

  • 如果文档只在当前文档中被引用,那么就应该在该文档的DTD的本地子集中定义它,这很像你在一个程序中定义一个方法或者内部类。

  • 如果实体要被多个文档引用,那么就应该将其定义为一个外部实体,与之对应的是,在程序中你将任何通用的类定义成一个外部类。

外部实体可以组成模块化的XML,这种XML更小、更易于更新和维护。这些外部实体还可以使得产生的文档具有更低的可见性,这就像是:一个良好的面向对象设计易于更改,但是难于一开始就将你的思想封装进去。

对于实体你甚至可以走极端。例如,你可以为一个单词“the”定义一个实体引用——虽然这样做对你没什么好处,但你的确可以这么做。


注意:一个实体越大,就越不会在对它进行更改时出现不希望看到的效果。例如,如果你定义的外部实体覆盖了安装说明书的一个完整的区域,那么对该区域的更改将不大会引起任何依赖于它的文档产生错误。虽然这样一来,小的内部代用会更加麻烦。例如,如果productName被定义为一个实体,那么可以对于不同部分的名称进行更改,而且这样做的确有效。假设产品名称

形如“HtmlEdit”。这是一个动词,于是你写一个句子,将其变成“You can HtmlEditor your life…”,这明显不能通过。然而,即使这样的代用有时会给你带来麻烦,但它们的确很节省时间。(一次替换就可以应用到名为productNoun、productVerb、productAdj和productAdverb的所有这些实体)


规格化DTD

就像你可以规格化你的XML文档一样,通过因子分解出公共块并且使用一个参数实体来引用它们,你也可以规格化DTD声明。该过程在Defining Parameter Entities and Conditional Sections(网址:http://java.sun.com/webservices/docs/1.1/tutorial/doc/JAXPSAX14.html#wp65381)上的SAX教程中有相关描述。通过因子分解出DTD(即模块化或者规格化),也会带来与规格化XML一样的好处和不利之处——改起来容易,统一起来有点困难。

你也可以建立conditionalized的DTD,正如SAX教程中Conditional Sections(网址:http://java.sun.com/webservices/docs/1.1/tutorial/doc/JAXPSAX14.html#wp65403)部分描述的那样。如果conditional区域的数量和大小与DTD的大小关联不大,你就可以将一个多种用途的DTD设置成“单源”。如果conditional区域逐渐变大,那么将会产生一个难于编辑的复杂的文档。

 

 

常见问答

下载中心

产品简介

 

 

Solaris论坛