xsd
  • schema
  • complextype
  • 2013-02-01 103 views 1 likes 
    1

    我想根据模式验证自定义XML文档。 本文档将包含具有任意数量元素的结构,每个元素都具有特定的属性。事情是这样的:是否可以使用任意名称定义具有特定属性的XSD元素

    <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> 
    

    回答

    2

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

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

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

    指定你似乎在XSD 1.1至所需的语言最简单的方法是将其说,记录元素定义的断言(1)记录每个孩子的“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的子元素,并要求实际的元素出现在Record的子元素被声明为可以替代这个抽象类型(这又要求它们的类型是从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等,并指定他们的替代记录,因此:

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

    在一定程度上,这符合你的描述,但我怀疑你不想不得不宣布FirstRecord,SecondRecord等

    已经描述的方法,使XSD可以做你的描述,我也说我不会推荐nd任何一种方法。相反,我会设计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“是元素类型名称而不是属性值。

    或者,可以说设计的重点在于允许记录包含任意结构元素的任意序列(在此帐户中,对xs:string的限制只是您的示例中的一个人为因素,并不是真正需要的实际上),只要我们对每条记录都有'attr'属性中记录的信息。容易够到指定此:定义“录制”作为与“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”属性的值可以被改变以'严格'或'跳过'或保持'宽松',这取决于你是否希望FirstRecord,SecondRecord等被验证(和声明)。

    +0

    这是一个非常详细的答案,非常感谢。据我所知,我无法访问.net世界中的符合XSD 1.1的解析器,所以我无法测试它。至于XSD 1.0的解决方法,这是接近但不完全,所以我不能使用它。然后,它将使用一个简单的无类型序列。额外的验证必须在应用程序级别执行。 –

    +0

    感谢您的替代建议,虽然没有在我的情况下做到这一点,其中包括实现一个基本的插件机制,由此每个插件将其属性保存在PropertyBag中,该PropertyBag被序列化为XML结构。在这种情况下,“记录”被替换为“属性”,“@attr”替换为“@vt”来指定属性的“变体类型”。每个插件可以具有任何数量的属性,每个属性都由@vt属性指定。解析器托管在我的框架中,而不是在每个插件中......因此需要一个固定的结构。 –

    +0

    XSD 1.0解决方案:比声明NOT POSSIBLE好得多:) –

    0

    我想这是不可能单独XSD!

    当你说

    任意数量的记录,每一个与他或她所选择的任意名称。

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

    所以..答案是NO!

    +0

    确实不可能用XSD 1.0,看来。根据C. M. Sperberg-McQueen的回答,似乎XSD 1.1是可能的,尽管我无法在.net世界中验证,因为据我所知,Microsof的解析器不支持它。 –

    +0

    @MaximeLabelle,.Net不支持XSD 1.1!但是撒克逊人,而.Net支持撒克逊! –

    相关问题