1

我正在尝试实现一个 XML Schema,它将强制执行以下 XML;

<databases>
    <database>
        <name>"Test A"</name>
        <host>"192.168.0.100"</host>
        <default>yes</default>
    </database>
    <database>
        <name>"Test B"</name>
        <host>"192.168.0.200"</host>
        <default>no</default>        
    </database>
    <database>
        <name>"Test C"</name>
        <host>"localhost"</host>
        <default>no</default>        
    </database>
</databases>

除了一个关键问题外,我能够自己实现 XML Schema;也就是最多只能将一个数据库标记为默认值。这意味着零数据库可以被标记为默认值,这也应该被认为是有效的。

例如,以下 XML 应被 XML Schema 视为无效,因为多个数据库被标记为默认值。

<databases>
    <database>
        <name>"Test A"</name>
        <host>"192.168.0.100"</host>
        <default>yes</default>
    </database>
    <database>
        <name>"Test B"</name>
        <host>"192.168.0.200"</host>
        <default>no</default>        
    </database>
    <database>
        <name>"Test C"</name>
        <host>"localhost"</host>
        <default>yes</default>        
    </database>

鉴于以下 XML 应被 XML Schema 视为有效,因为没有(零)数据库被标记为默认值;

<databases>
    <database>
        <name>"Test A"</name>
        <host>"192.168.0.100"</host>
        <default>no</default>
    </database>
    <database>
        <name>"Test B"</name>
        <host>"192.168.0.200"</host>
        <default>no</default>        
    </database>
    <database>
        <name>"Test C"</name>
        <host>"localhost"</host>
        <default>no</default>        
    </database>

有谁知道是否可以使用 XML 模式强制执行这样的约束?我觉得它应该是,但我不知道如何去实施它。

对于此事的任何帮助将不胜感激。

提前致谢。

4

1 回答 1

1

检查是否只有一个数据库被标记为默认数据库的最简单方法可能是对 XML 进行不同的构造:如上记录数据库,但删除default元素,并向databases标识默认数据库的元素添加属性或子元素。您的 XML 变为:

<databases default="Test A">
    <database>
        <name>Test A</name>
        <host>"192.168.0.100"</host>
   </database>
    <database>
        <name>Test B</name>
        <host>"192.168.0.200"</host>
   </database>
    <database>
        <name>Test C</name>
        <host>"localhost"</host>
   </database>
</databases>

(我已经删除了元素中的引号name,理论上它们实际上不是数据库名称的一部分。如果它们是名称的一部分,那么默认属性将需要类似于default='"Test A"',包括引号。)

您需要确保database/name元素是唯一的;使用元素xs:key声明中的构造来执行此操作。databases您还需要确保可选default属性指向数据库名称;用xs:keyref. 的声明databases可能看起来像这样:

<xs:element name="databases" type="databases">
  <xs:key name="dbname">
    <xs:selector xpath="database"/>
    <xs:field xpath="name"/>
  </xs:key>
  <xs:keyref refer="dbname" name="defaultdb">
    <xs:selector xpath="."/>
    <xs:field xpath="@default"/>
  </xs:keyref>
</xs:element>

第二种方法对xs:unique. 不要让databasebe的第三个孩子default使用值yesor no,而是让第三个孩子成为命名元素default-database或命名元素non-default-database(当然,更改名称以适合您的喜好)。以确保每个实例default-database具有相同简单类型值的方式定义这些。

然后指定没有两次出现的default-database可能具有相同的字符串值,使用xs:unique

<xs:unique name="dbname">
  <xs:selector xpath="database"/>
  <xs:field xpath="default-database"/>
</xs:unique>

由于 的每个实例都default-database具有相同的值,因此这种唯一性约束确保只能存在一个这样的元素。整个架构可能看起来像这样。首先是簿记:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  elementFormDefault="qualified">

现在的元素声明databases

  <xs:element name="databases" type="databases">
    <xs:unique name="dbname">
      <xs:selector xpath="database"/>
      <xs:field xpath="default-database"/>
    </xs:unique>
  </xs:element>

及其复杂类型:

  <xs:complexType name="databases">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:element ref="database"/>
    </xs:sequence>
    <xs:attribute name="default" type="xs:string" use="optional"/>
  </xs:complexType>

现在再做一些簿记:声明database,只是为了让我们诚实。

  <xs:element name="database" type="database"/>

  <xs:complexType name="database">
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="host" type="xs:string"/>
      <xs:choice>       
        <xs:element name="default-database" type="empty"/>
        <xs:element name="non-default-database" type="empty"/>
      </xs:choice>
    </xs:sequence>
  </xs:complexType>

最后一点记账:一种确保 的每个实例都default-database具有相同值的类型。(当然,还有其他方法可以实现这一点;这只是首先想到的。)

  <xs:simpleType name="empty">
    <xs:restriction base="xs:string">
      <xs:enumeration value=""/>
    </xs:restriction>
  </xs:simpleType> 

</xs:schema>

如果您绝对与现有设计结婚,那么我不知道如何在 XSD 1.0 中做您想做的事情;如果您可以使用 XSD 1.1,当然,您可以在上面添加一个databases断言

<xs:assert test="count(database[default = 'yes']) = 1"/>

但正如上面的替代解决方案所示,断言的设计和使用并不是你唯一的选择。

于 2012-10-28T02:05:06.010 回答