一个共同的问题是,在开发、测试和开发环境之间需要改变参数。尤其讨厌的是,当参数是目录或文件系统路径时必须有多处改变。让我们来看使用内部实体如何使这些配置变得轻松。
我们以创建这个问题的一个简单的例子开始。清单1显示一个小的应用程序XML配置文件。正如你所看到的,这里有多个参数都引用了一个物理文件系统路径。从一台机器移动这个配置到另一个上也许要求你重新录入每一个参数!
清单1: myapp.xml
<?xml version="1.0" ?>
<MyConfiguration>
<Templates>
<Template name="temp1">C:MyAppTemplates emplate1.xsl</Template>
<Template name="temp2">C:MyAppTemplates emplate2.xsl</Template>
<Template name="temp3">C:MyAppTemplates emplate3.xsl</Template>
</Templates>
<Logs>
<Log name="stdout">C:MyAppLogsmyapp.log</Log>
<Log name="error">C:MyAppLogserror.log</Log>
</Logs>
</MyConfiguration>
紧记这只是一个过分单纯化的例子。一个真实的配置文件也许有上百行长。同样,也不像这里的这些路径式的参数都很好的组织在一起。
为了解决这个问题,我们使用“内部实体(internal entities)”。你也许对XML实体已经熟悉。基本上,实体是作为XML文档中一段数据常被做为引用而不是字面上的内容。一个很好的例子是“greater-than”符号。它是XML中一个特殊的字符,你不能简单的把它放到你的文档中。代替的是,你必须引用它与XML实体关联。胜于在文档中使用>,你用>。
每个XML有这种形式(&记号-名字-分号)。在XML文档中有五个预先定义的实体可用,它们是:
我们可以很容易地在XML文档中定义新的实体,称为“内部子集(internal subset)”。内部子集是在XML 文档内部定义DTD的子集。这里有在XML文件中简单DTD声明的一个例子:
<!DOCTYPE MyConfiguration SYSTEM "MyConfiguration.dtd">
这个声明仅仅引用了一个外部DTD的子集。我们想要加入一个内部子集到这个DTD中。只需在访括号内附加我们的内部实体声明并且在DOCTYPE声明里面实现它。下面是例子:
<!DOCTYPE MyConfiguration SYSTEM "MyConfiguration.dtd" [
<!ENTITY MyEntity "the value of the entity">
]>
注意,因为ENTITY和DOCTYPE声明都是DTD规范(并不是XML规范)的一部分,他们没有结束标记和斜线。你也许不用DTD验证你的文档,这种情况下,你不必有外部子集。这里是只有内部子集的一个例子:
<!DOCTYPE MyConfiguration [
<!ENTITY MyEntity "the value of the entity">
]>
此刻,我们的配置文件有Template和Log元素内容的字面文本。为了使用实体引用,我们将要在文档的子集中定义一个新的实体。因为我们目前不用DTD来验证这个配置文件,我们只是简单的像这样使用内部子集:
<!DOCTYPE MyConfiguration [
<!ENTITY InstallDir "C:MyApp">
]>
现在,我们可以创建一个新的配置文档,只有一个地方表述软件被安装的路径可以修改。清单2显示修订后XML文件。
清单2: myapp.xml
<?xml version="1.0" ?>
<!DOCTYPE MyConfiguration [
<!ENTITY InstallDir "C:MyApp">
]>
<MyConfiguration>
<Templates>
<Template name="temp1">&InstallDir;Templates emplate1.xsl</Template>
<Template name="temp2">&InstallDir;Templates emplate2.xsl</Template>
<Template name="temp3">&InstallDir;Templates emplate3.xsl</Template>
</Templates>
<Logs>
<Log name="stdout">&InstallDir;Logsmyapp.log</Log>
<Log name="error">&InstallDir;Logserror.log</Log>
</Logs>
</MyConfiguration>