1

我想根据模式验证自定义 XML 文档。该文档将包括具有任意数量元素的结构,每个元素都有特定的属性。像这样的东西:

<Root xmlns="http://tns">
  <Records>
    <FirstRecord attr='whatever'>content for first record</FirstRecord>
    <SecondRecord attr='whatever'>content for first record</SecondRecord>
    ...
    <LastRecord attr='whatever'>content for first record</LastRecord>
  </Records>
</Root>

XML 文档的作者可以包含任意数量的记录,每条记录都有他或她选择的任意名称。这怎么可能根据 XML Schema 来验证呢?

我试图在架构中指定适当的结构类型,但我不知道如何在适当的位置引用它:

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

    <xs:complexType name="RecordType"> <!-- This is my record type -->
        <xs:simpleContent>
            <xs:extension base="xs:string">
            <xs:attribute name="attr" type="xs:string" use="required" /> 
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>

    <xs:element name="Root">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="1" maxOccurs="1" name="Records">
                    <!-- This is where records should go -->
                    <xs:complexType /> 
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>
4

2 回答 2

2

您所描述的在 XSD 1.1 中是可能的,在 XSD 1.0 中也可能有非常相似的东西,这并不是说这是一个可取的设计。

在 XML 词汇表中,元素类型通常传达有关信息类型的相关信息,它是元素类型的名称,用于驱动大多数 XML 模式语言中的验证;您描述的设计(有些人会说)有点像问我是否可以在 Java 中定义一个对象类,或者在 C 中定义一个结构,它遵守成员可以具有任意名称的约束,只要其中一个是一个值为 42 的整数。这或类似的东西很可能是可能的,但大多数有经验的设计师会强烈认为这几乎肯定不是解决任何正常问题的正确方法。

另一方面,用系统做不寻常和尴尬的事情有时可以帮助学习如何有效地使用系统。(你永远不会很好地了解一个系统,我的一个朋友曾经说过,直到你彻底滥用它。)所以我的回答有两个部分:如何尽可能接近你在 XSD 中指定的设计,以及你可能考虑的替代方案反而。

在 XSD 1.1 中指定您似乎想要的语言的最简单方法是在 Records 元素上定义一个断言,它表示 (1) Records 的每个子项都有一个“attr”属性,并且 (2) Records 的子项没有任何属性孩子们。你会有这样的东西:

...
<xs:element minOccurs="1" maxOccurs="1" name="Records">
  <xs:complexType>
    <xs:sequence>
      <xs:any/>
    </xs:sequence>
    <xs:assert 
      test="every $child in * satisfies $child/@attr"/>
    <xs:assert 
      test="not(*/*)"/>       
  </xs:complexType> 
</xs:element>
...

如您所见,这与 InfantPro'Aravind' 所描述的非常相似;它通过使用断言而不是类型分配来施加您施加的约束,从而避免了 InfantPro'Aravind' 确定的问题。

在 XSD 1.0 中,断言不可用,我能想到的接近您描述的设计的唯一方法是定义一个抽象元素,我将其称为 Record,作为 Records 的子元素,并要求元素实际上作为 Records 的子级出现的,被声明为可以替代这个抽象类型(这反过来又要求它们的类型派生自 RecordType 类型)。您的架构可能会这样说:

<xs:element name="Root">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Records">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="Record"/>
          </xs:sequence>
        </xs:complexType> 
      </xs:element>        
    </xs:sequence>
  </xs:complexType>
</xs:element>

<xs:complexType name="RecordType"> 
  <xs:simpleContent>
    <xs:extension base="xs:string">
      <xs:attribute name="attr" 
                    type="xs:string" 
                    use="required" /> 
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

<xs:element name="Record" 
            type="RecordType" 
            abstract="true"/>

在架构的其他地方(可能在单独的架构文档中),您将需要声明 FirstRecord 等,并指定它们可替代 Record,因此:

<xs:element name="FirstRecord" substitutionGroup="Record"/>
<xs:element name="SecondRecord" substitutionGroup="Record"/>
<xs:element name="ThirdRecord" substitutionGroup="Record"/>
...

在某种程度上,这符合您的描述,尽管我怀疑您不想声明 FirstRecord、SecondRecord 等。

在描述了 XSD 可以执行您所描述的方式之后,我还应该说我不会推荐这两种方法中的任何一种。相反,我会以不同的方式设计 XML 词汇表,以便更自然地使用 XSD。

在您指定的设计中,每条记录似乎都具有相同的类型,但除了元素的内容之外,它们还可以通过具有不同的名称(FirstRecord、SecondRecord 等)来传达一定数量的额外信息。 . 这些附加信息可以很容易地在属性中传达,这将允许您将 Record 指定为具体元素,而不是抽象元素,从而为其提供额外的“替代名称”属性。然后,您的示例数据将采用如下形式:

<Root xmlns="http://tns">
  <Records>
    <Record 
      alternate-name="FirstRecord"
      attr='whatever'>content for first record</Record>
    <Record
      alternate-name="SecondRecord"
      attr='whatever'>content for first record</Record>
    ...
    <Record 
      alternate-name="LastRecord"
      attr='whatever'>content for first record</Record>
  </Records>
</Root>

这将或多或少可以接受,具体取决于您或您的数据提供者或工具链中的工具是否将字符串“FirstRecord”作为元素类型名称而不是属性值附加了一些神秘或其他意义。

或者,可以说设计的重点是允许 Records 包含任意结构的任意元素序列(因此,限制xs:string只是您的示例的工件,实际上并不真正需要)正如我们所拥有的,对于每条记录,“attr”属性中记录的信息。很容易指定这一点:将“记录”定义为具有“属性”属性的具体元素,接受一个可以是任何 XML 元素的子元素:

<xs:element name="Record">
  <xs:complexType>
    <xs:sequence>
      <xs:any processContents="lax"/>
    </xs:sequence>
    <xs:attribute name="attr" 
                  use="required" 
                  type="xs:string"/>
  </xs:complexType>
</xs:element>

'processContents' 属性的值可以更改为'strict' 或'skip' 或保持在'lax',这取决于您是否想要验证(和声明)FirstRecord、SecondRecord 等。

于 2013-02-01T18:19:53.940 回答
0

我想这仅靠 XSD 是不可能的!

当你说

任意数量的记录,每个记录都有他或她选择的任意名称。

这迫使我们使用<xs:any/>元素但是!将元素声明为any不允许您验证其下的属性..

所以..答案是否定的!

于 2013-02-01T13:35:18.423 回答