【问题标题】:Is it possible to define a XSD element with an arbitrary name but with specific attributes是否可以定义具有任意名称但具有特定属性的 XSD 元素
【发布时间】:2013-02-02 02:19:53
【问题描述】:

我想根据模式验证自定义 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>

【问题讨论】:

    标签: xsd schema complextype


    【解决方案1】:

    您所描述的在 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、 ETC。)。这些附加信息可以很容易地在属性中传达,这将允许您将 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 等。

    【讨论】:

    • 这是一个非常详细的答案,非常感谢。据我所知,我无法访问.net 世界中符合 XSD 1.1 的解析器,因此我无法对此进行测试。至于 XSD 1.0 解决方法,这很接近但不完全,所以我不能使用它。然后,它将处理一个简单的无类型 序列。必须在应用程序级别执行额外的验证。
    • 感谢其他建议,尽管在我的情况下没有一个可以做,它包括实现一个基本的插件机制,每个插件将其属性保存在一个 PropertyBag 中,该 PropertyBag 被序列化为 XML 结构。在这种情况下,“记录”被“属性”替换,“@attr”被“@vt”替换以指定属性的“变体类型”。每个插件可以有任意数量的属性,每个属性都是任意类型的,由@vt 属性指定。解析器托管在我的框架中,而不是每个插件中......因此需要固定结构。
    • XSD 1.0 解决方案:比声明 NOT POSSIBLE 好得多 :)
    【解决方案2】:

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

    当你说

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

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

    所以..答案是否定的!

    【讨论】:

    • 似乎确实不可能使用 XSD 1.0。根据 C. M. Sperberg-McQueen 的回答,XSD 1.1 似乎是可能的,尽管我无法在 .net 世界中验证,因为据我所知,微软的解析器不支持它。
    • @MaximeLabelle,.Net 不支持 XSD 1.1!但 Saxon 支持,而且 .Net 支持 Saxon!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2018-02-09
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 2014-06-06
    相关资源
    最近更新 更多