如何使用XML Schema验证具有值约束的XML,还是应该?

时间:2009-12-08 11:06:31

标签: xml xsd

我很谨慎,这可能会产生讨论而不是答案,但不过......

我目前有一个XML,我设计的主要目标是使其简洁易读;这对我来说意味着偏爱元素属性并最大限度地减少词汇量:

<?xml version='1.0'?>

<Calculation jobId='XI5-332123' user='wombat' time='2009-04-22T14:04:00Z' version='1'>
    <Model fileName='Simulate_TestModelSpecifications' processName='Simulate_TestModelSpecifications' password='Simulate_TestModelSpecifications'/>
    <Simulation>
        <ModelSpecification name='realParam' type='real' value='5.4864'/>
        <ModelSpecification name='intParam' type='integer' value='7'/>
        <ModelSpecification name='boolParam' type='boolean' value='true'/>
        <ModelSpecification name='realArrayParam' type='real' numElements='2'>
            <ArrayElement value='1.0'/>
            <ArrayElement value='2.0'/>
        </ModelSpecification>
        <ModelSpecification name='intArrayParam' type='integer' numElements='2'>
            <ArrayElement value='5'/>
            <ArrayElement value='6'/>
        </ModelSpecification>
        <ModelSpecification name='boolArrayParam' type='boolean' numElements='2'>
            <ArrayElement value='true'/>
            <ArrayElement value='false'/>
        </ModelSpecification>
        <ModelSpecification name='Var1' type='real' value='20.0'/>
        <ModelSpecification name='Var2' type='real' numElements='2'>
            <ArrayElement value='30.0'/>
            <ArrayElement value='40.0'/>
        </ModelSpecification>
        <ModelSpecification name='scalarSelector' type='string' value='apple'/>
        <ModelSpecification name='arraySelector' type='string' numElements='3'>
            <ArrayElement value='red'/>
            <ArrayElement value='yellow'/>
            <ArrayElement value='blue'/>
        </ModelSpecification>
        <ReportVariable pathName='myUnit.Var1'/>
        <ReportVariable pathName='myUnit.Var2(1)'/>
        <ReportVariable pathName='myUnit.Var2(2)'/>
    </Simulation>
</Calculation>

现在出现的问题是它是否可以通过XSD完全验证?如果不能,那么一些验证是否必须在SAX解析器(也在我的控制下)中实现是否重要?

我对XML模式的经验非常(非常)有限,但据我所知,验证此XML有3个潜在的棘手问题:

  • <ArrayElement>的'value'属性类型由父<ModelSpecification>的'type'属性值控制。
  • <ModelSpecification>必须具有'value'属性或'numElements'属性,但不能同时具有两者。
  • 只允许具有'numElements'属性的<ModelSpecification>包含<ArrayElement>,并且这些元素的数量仅限于该属性的值。

所以我的问题是:

  1. 我是否认为XSD无法强制执行这3项?
  2. 如果我把它们强加在SAX解析器中,这有关系吗?
  3. 谢谢,

    汤姆

3 个答案:

答案 0 :(得分:3)

可能编写模式来验证给定的XML(稍作修改),但仅限于某些条件。也就是说,您的第三个项目符号点不能像您描述的那样在XML模式中定义(使用numElements值本身来限制ArrayElements的数量)。但是,如果您知道numElements只有某些值,则可以创建与每个选项对应的模式元素。如果numElements可能很大或可能会随着时间的推移而增长,那么这对您来说可能不是一个好选择。

我也同意numElements是多余的(类型也是如此)。但是我让他们完整了。

我想在输入之前向自己证明这一点,所以我实际上最终编写了整个架构。无论如何我都这样做了,我也可以传递它:)有两种编写XML的方法可以用这个模式进行验证。你担心它是人类可读的,我不知道你更喜欢哪个选项。 (为了空间的利益,我省略了Real和Bool类型)。

第一个选项跳过xsi:type并直接重命名<ModelSpecification>元素(感谢替换组):

<Calculation xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Model />
  <Simulation>
    <OneValueIntegerModel name='intParam' type='integer' value='7'/>
    <TwoValueIntegerModel name="intArrayParam" type="integer" numElements="2">
      <ArrayElement value="5" />
      <ArrayElement value="6" />
    </TwoValueIntegerModel>
    <OneValueStringModel name='scalarSelector' type='string' value='apple'/>
    <ThreeValueStringModel name='arraySelector' type='string' numElements='3'>
      <ArrayElement value='red'/>
      <ArrayElement value='yellow'/>
      <ArrayElement value='blue'/>
    </ThreeValueStringModel>
    <ReportVariable pathName='myUnit.Var1'/>
    <ReportVariable pathName='myUnit.Var2(1)'/>
    <ReportVariable pathName='myUnit.Var2(2)'/>
  </Simulation>
</Calculation>

第二个选项保留名为<ModelSpecification>的元素并使用xsi:type来声明它是哪种类型:

<Calculation xmlns="http://stackoverflow-sample" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Model />
  <Simulation>
    <ModelSpecification xsi:type="OneValueIntegerModel" name='intParam' type='integer' value='7'/>
    <ModelSpecification xsi:type="TwoValueIntegerModel" name="intArrayParam" type="integer" numElements="2">
      <ArrayElement value="5" />
      <ArrayElement value="6" />
    </ModelSpecification>
    <ModelSpecification xsi:type="OneValueStringModel" name='scalarSelector' type='string' value='apple'/>
    <ModelSpecification xsi:type="ThreeValueStringModel" name='arraySelector' type='string' numElements='3'>
      <ArrayElement value='red'/>
      <ArrayElement value='yellow'/>
      <ArrayElement value='blue'/>
    </ModelSpecification>
    <ReportVariable pathName='myUnit.Var1'/>
    <ReportVariable pathName='myUnit.Var2(1)'/>
    <ReportVariable pathName='myUnit.Var2(2)'/>
  </Simulation>
</Calculation>

可用于验证这两个选项的架构如下:

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

  <xs:element name="Calculation">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Model" />
        <xs:element name="Simulation">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="ModelSpecification" minOccurs="1" maxOccurs="unbounded" />
              <xs:element name="ReportVariable" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:attribute name="pathName" type="xs:string" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:element name="ModelSpecification" type="ModelSpecification" abstract="true" />
  <xs:complexType name="ModelSpecification" abstract="true">
    <xs:attribute name="name" type="xs:token" use="required" />
    <xs:attribute name="type" type="xs:QName" use="required" />
  </xs:complexType>

  <!--Integer Model-->

  <xs:complexType name="IntegerArrayElement">
    <xs:attribute name="value" type="xs:integer" use="required" />
  </xs:complexType>

  <xs:element name="OneValueIntegerModel" type="OneValueIntegerModel" substitutionGroup="ModelSpecification" />
  <xs:complexType name="OneValueIntegerModel">
    <xs:complexContent>
      <xs:extension base="ModelSpecification">
        <xs:attribute name="value" type="xs:integer" use="required" />
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:element name="TwoValueIntegerModel" type="TwoValueIntegerModel" substitutionGroup="ModelSpecification" />
  <xs:complexType name="TwoValueIntegerModel">
    <xs:complexContent>
      <xs:extension base="ModelSpecification">
        <xs:sequence>
          <xs:element name="ArrayElement" type="IntegerArrayElement" minOccurs="2" maxOccurs="2" />
        </xs:sequence>
        <xs:attribute name="numElements" type="xs:integer" use="required" fixed="2" />
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <!--String Model-->

  <xs:complexType name="StringArrayElement">
    <xs:attribute name="value" type="xs:string" use="required" />
  </xs:complexType>

  <xs:element name="OneValueStringModel" substitutionGroup="ModelSpecification" />
  <xs:complexType name="OneValueStringModel">
    <xs:complexContent>
      <xs:extension base="ModelSpecification">
        <xs:attribute name="value" type="xs:string" use="required" />
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:element name="ThreeValueStringModel" type="ThreeValueStringModel" substitutionGroup="ModelSpecification" />
  <xs:complexType name="ThreeValueStringModel">
    <xs:complexContent>
      <xs:extension base="ModelSpecification">
        <xs:sequence>
          <xs:element name="ArrayElement" type="StringArrayElement" minOccurs="3" maxOccurs="3" />
        </xs:sequence>
        <xs:attribute name="numElements" type="xs:integer" use="required" fixed="3" />
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

</xs:schema>

答案 1 :(得分:1)

是的,XSD不支持您为该XML语法提出的[1,3]场景。对于方案2,您可以使用<xsd:choice>和min / maxOccurs = 1。

您还可以将元素语法更改为不是类型变体并创建该验证。

如果您使用SAX解析器解析XML,则需要知道该结构并对其进行验证。所以,我认为这种情况下你可以放弃XSD验证。

但是,如果是其他人创建该XML文件,那么XSD可能非常方便,因为它可以在到达您的系统之前在外部进行验证。

答案 2 :(得分:1)

当type =“integer”时,不可能直接验证,值不能为“1.23”。您可以做的是首先将此XML转换为可以更严格验证的内容,或者以这种方式更改初始方案。

我认为属性“numElements”是一个冗余属性btw,因为你可以读取/反序列化并计算元素。

<ModelSpecification name="realParam">
    <RealParam>1.234</RealParam>
</ModelSpecification>
<ModelSpecification name='intParam'>
    <IntParam>7</IntParam>
</ModelSpecification>
<ModelSpecification name='realArrayParam'>
    <Array>
        <RealParam>1.2</RealParam>
        <RealParam>2.1</RealParam>
    </Array>
</ModelSpecification>
相关问题