我想针对架构验证自定义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>
答案 0 :(得分:2)
您在XSD 1.1中描述的内容是可行的,XSD 1.0中可能会有类似的内容,这并不是说它是一个可取的设计。
在XML词汇表中,元素类型通常传达有关信息类型的相关信息,它是用于在大多数XML模式语言中驱动验证的元素类型的名称;您描述的设计(有人会说)有点像询问我是否可以在Java中定义一个对象类,或者在C中定义一个结构,它遵循成员可以拥有任意名称的约束,只要其中一个是一个值为42的整数。这或类似的东西很可能,但大多数有经验的设计师会强烈地感到这几乎肯定不是解决任何正常问题的正确方法。
另一方面,使用系统做一些不寻常和尴尬的事情有时可以帮助学习如何有效地使用系统。 (你永远不会对系统有所了解,我的朋友曾经说过,直到你彻底滥用它。)所以我的答案分为两部分:如何尽可能接近你在XSD中指定的设计,以及你可能考虑的替代方案代替。
在XSD 1.1中指定您似乎想要的语言的最简单方法是在Records元素上定义一个断言,该断言说(1)Records的每个子节点都有'attr'属性,(2)没有子节点唱片有任何孩子。你会有这样的事情:
...
<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指定为具体元素,而不是抽象元素,从而为其提供额外的“alternate-name”属性。然后,您的样本数据将采用以下形式:
<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”成为元素类型名称而不是属性值。
或者,可以说设计的要点是允许记录包含任意结构的任意元素序列(在此帐户中,对xs:string
的限制只是您示例的一个工件,并且是只要我们对每个记录都有“attr”属性中记录的信息,实际上并不是真正需要的。很容易指定:将'Record'定义为具有'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等。
答案 1 :(得分:0)
我想单独使用XSD是不可能的!
当你说
时任意数量的记录,每个记录都有他或她选择的任意名称。
这迫使我们使用<xs:any/>
元素但是!将元素声明为any
不允许您验证其下的属性..
所以..答案是否定的!