【问题标题】:XML define restrictions without affecting uniquenessXML 定义限制而不影响唯一性
【发布时间】:2014-08-30 21:30:44
【问题描述】:

下面是一个示例 XML 架构。如您所见,我需要创建大量元素(elem001elem999),其中所有元素都引用另一个元素“param”。请注意,param 的属性 ID 是一个键,因此必须是唯一的。

现在,我如何指定一个限制,使得对于elem001,元素“param”的属性“数量”只能采用给定的一组值。同样,elem002elem999 将有自己的一组值。但是,整个 XML 文档中的所有“param”元素都必须具有唯一的 ID。

我猜如果我使用类型派生限制而不是 ref,则唯一性约束将不再适用于所有“param”子类型。那我该怎么做呢?

<xsd:element name="param">
    <xsd:complexType>
        <xsd:simpleContent base="xsd:double">
            <xsd:extension>
                <xsd:attribute name="quantity" type="xsd:string">
                <xsd:attribute name="ID" type="xsd:int">
            </xsd:extension>
        </xsd:simpleContent>
    </xsd:complexType>
</xsd:element>

<xsd:root>
    <xsd:key name="paramkey">
        <xsd:selector xpath="param/">
        <xsd:field xpath="@ID">
    </xsd:key>

    <xsd:element name="elem001">
        <xsd:element ref="param">
        <xsd:element name="X" />
    </xsd:element>

    <xsd:element name="elem002">
        <xsd:element ref="param">
        <xsd:element name="Y" />
    </xsd:element>

    <!-- ... -->

    <xsd:element name="elem999">
        <xsd:element ref="param">
        <xsd:element name="Y" />
    </xsd:element>
</xsd:root>

---------------编辑----

很抱歉,我应该提供我的最终目标的示例 XML 文件以使其更清晰。在下面的示例中,您可以看到我有几个包含(除其他外)一些物理参数的对象(汽车、飞机、马)。属性“数量”定义了我们所说的物理量:温度、燃料水平等。

现在,我有两个要求:

首先,我需要在我的架构中限制允许的汽车物理量,这显然不能包括血压。同样,指定马的高度也没有意义(至少对于我的应用程序......)。因此,我需要指定允许在汽车上测量的物理量。

其次,参数的 id 在所有参数中必须是唯一的。例如,飞机高度的id不能为6,如果id为6的参数(马的血压)在“数量”中有另一个字符串值

<car>
    <param id="1" quantity="fuelLevel">100</param>
    <param id="2" quantity="temperature">86</param>
</car>
<car>
    <param id="3" quantity="fuelLevel">99</param>
    <param id="4" quantity="temperature">77</param>
</car>
<horse>
    <param id="5" quantity="temperature">123</param>
    <param id="6" quantity="bloodPressure">26</param>
</horse>
<plane>
    <param id="7" quantity="altitude">1000</param>
    <param id="8" quantity="temperature">1050</param>
</plane>

其实,如果我能做下面这样的事情,可能会更好,但我认为它更不可行。

<car>
    <params>
        <fuelLevel id="1">100</fuelLevel>
        <temperature id="2">86</temperature>
    </params>
</car>
<car>
    <params>
        <fuelLevel id="3">99</fuelLevel>
        <temperature id="4">77</temperature>
    </params>
</car>
<horse>
    <params>
        <temperature id="5">123</temperature>
        <bloodPressure id="6">26</bloodPressure>
    </params>
</horse>
<plane>
    <params>
        <temperature id="7">1000</temperature>
        <altitude id="8"></altitude>
    </params>
</plane>

另一个精度:我需要唯一的 id 是一个 int,所以我不能使用关联 id 和数量字段的键。

顺便说一句,这个论坛不允许我写一些问候或感谢的表述,这很令人不安。

【问题讨论】:

  • elem001 等是否在其他地方引用 param 元素?还是他们将param 元素嵌入为子元素?完全不清楚您的 XML 应该是什么样子;如果没有更明确的问题,就不可能回答您的问题。

标签: xml xsd schema


【解决方案1】:

你写我猜如果我使用类型派生限制而不是引用,唯一性约束将不再适用于所有“param”子类型。你为什么这么猜? (而且检查规范或询问验证器不是比猜测更有用吗?)

伪模式中的键约束适用于名为param 的作用域元素的所有子元素,无论其类型如何;为什么类型限制会改变呢?

在任何情况下,类型限制和 XSD 标识约束在这里是否有任何相关性都不清楚:如果你想限制给定元素可以指向的 param 元素集,而不是通过所指向元素的名称at 或其在文档中的位置,但是基于指向 param 元素上的 quantity 属性的值,您已经远远超出了基本 XSD 类型构造函数的表达能力,也超出了XSD 身份约束的表达能力。 (此外,它的价值超出了关系数据库系统中参照完整性约束的可比性。)

XSD 标识约束确保在文档中给定元素实例范围内的 XPath 1.0 的一个非常简单、清晰可流的子集中可识别的值或值序列的唯一性。

XSD 类型定义总是被构造成允许以独立的类型验证元素,以一种上下文无关的方式。没有类型级别的约束可以依赖于正在验证的元素或属性之外的值——如果可以,那么将元素插入特定上下文或检索它并从其上下文中提取它的操作可能会更改其类型。询问离您最近的数据库或编程语言理论家他们希望如何,但要准备好在他们晕倒时抓住他们。

你的设计听起来很可怕、没有必要、毫无意义地令人费解。你几乎肯定应该把它扔掉并重新开始。它不适合作为使用 XML 的设计。

它最多可能是一种练习,一种展示如何处理 XSD 情况的方法,如果你需要蒙上自己的眼睛,把手放在口袋里,一边哼着马赛曲,一边单腿跳当房间里的其他人在你耳边喊达达诗,把香蕉皮塞到你的脚下。

本着这种精神,请考虑以下 XSD 1.1 架构,它定义了满足您在散文中描述的要求的词汇表(忽略您的伪代码,因为我无法弄清楚它要做什么)。

我们定义元素 rootparamelem001elem005,将其他 994 留给读者练习。

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.com/jacques"
  xmlns:tns="http://example.com/jacques"
  elementFormDefault="qualified"> 

  <xs:element name="root" type="tns:root">
    <xs:key name="parameter-uniqueness">
      <xs:selector xpath=".//tns:param"/>
      <xs:field xpath="@ID"/>
    </xs:key>    
  </xs:element>

如您所见,根元素强制 param/@ID 值的唯一性。

  <xs:element name="param" type="tns:param"/>
  <xs:element name="elem001" type="tns:elemNNN"/>
  <xs:element name="elem002" type="tns:elemNNN"/>
  <xs:element name="elem003" type="tns:elemNNN"/>
  <xs:element name="elem004" type="tns:elemNNN"/>
  <xs:element name="elem005" type="tns:elemNNN"/>

paramelemNNN 元素不是很有趣。请注意,elem001elem005 都共享相同的类型。类型限制在这里不是不可能的,但它也无助于实现你所说的你想要的。

  <xs:complexType name="param">
    <xs:simpleContent>
      <xs:extension base="xs:double">
        <xs:attribute name="quantity" type="xs:integer"/>
        <xs:attribute name="ID" type="xs:int"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="elemNNN" mixed="false">
    <xs:sequence>
      <!--* Whatever common structure we want here *-->
      <xs:any minOccurs="0" maxOccurs="unbounded" 
              processContents="lax"/>
    </xs:sequence>    
    <xs:attribute name="param" type="xs:int" use="required"/>    
    <!--* Whatever common attributes we want ... *-->
    <xs:attribute name="color"/>    
    <xs:attribute name="shape"/>
    <xs:attribute name="height"/>
    <xs:attribute name="postal-code"/>
    <!--* ... *-->
  </xs:complexType>

所有有趣的动作都在根元素的类型中。它的内容模型很简单:一些参数,然后是elemNNN 元素:

  <xs:complexType name="root">
    <xs:sequence>
      <xs:element ref="tns:param" minOccurs="0"
                  maxOccurs="unbounded"/>      
      <xs:element ref="tns:elem001"/>  
      <xs:element ref="tns:elem002"/>  
      <xs:element ref="tns:elem003"/>  
      <xs:element ref="tns:elem004"/>  
      <xs:element ref="tns:elem005"/>
    </xs:sequence>

给定元素可以指向的param 元素的约束不能根据指向元素的类型制定,因为param 元素在指向元素之外。 (如果它们是嵌入的,正如您的伪模式似乎暗示的那样,事情会非常不同,但不会涉及指向。)因此,它们必须在一些具有指向元素和参数的祖先元素的声明中制定元素作为后代。在这里,我们在根元素上进行。

让我们允许elem001 仅指向param 属性在-323 和+2387 之间(含)的param 元素:

    <xs:assert test="for $e in .//tns:elem001, 
      $pref in $e/@param,
      $p in .//tns:param[@ID = $pref],
      $q in $p/@quantity
      return 
      ($q gt -324 and $q le 2387) (: in range -323 to 2398 :) "/>

元素elem002 只允许指向param 元素,其quantity 属性是小于15 的正素数:

    <xs:assert test="for $e in .//tns:elem002,  
      $pref in $e/@param,
      $p in .//tns:param[@ID = $pref],
      $q in $p/@quantity
      return 
      ($q  = (2, 3, 5, 7, 11, 13)) (: prime lt 15 :) "/>

elem003 的约束是其param 元素的quantity 值必须等于0、1、7、8、14、15、...:

    <xs:assert test="for $e in .//tns:elem003,
      $pref in $e/@param,
      $p in .//tns:param[@ID = $pref],
      $q in $p/@quantity
      return 
      ($q mod 7 lt 2) "/>

等等。

    <xs:assert test="for $e in .//tns:elem004,  
      $pref in $e/@param,
      $p in .//tns:param[@ID = $pref],
      $q in $p/@quantity
      return  
      ($q gt 2387)  "/>

    <xs:assert test="for $e in .//tns:elem005,
      $pref in $e/@param,
      $p in .//tns:param[@ID = $pref],
      $q in $p/@quantity
      return 
      ($q le -324)

      "/>
  </xs:complexType>

</xs:schema>

此架构接受以下有效输入;通过使用不适当的quantity 属性将指针指向param,或者更改实际指向的param 元素上的quantity 值,很容易使其无效。

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://example.com/jacques jacques.xsd" 
  xmlns="http://example.com/jacques">
  <param ID="1" quantity="23">2</param>
  <param ID="14" quantity="3">876.4</param>
  <param ID="31" quantity="44423">833838</param>
  <param ID="-44" quantity="23">111</param>
  <param ID="-127" quantity="-0333">22223.33</param>
  <param ID="22" quantity="8">2</param>
  <param ID="12" quantity="23">1</param>
  <elem001 param="1">
    <p>This is some sample data.</p>
  </elem001>
  <elem002 param="14">
    <p>Just hanging around here.</p>
  </elem002>
  <elem003 param="22">
    <p>No worries.</p>
  </elem003>
  <elem004 param="31">
    <p>How you doin', bro?</p>
  </elem004>
  <elem005 param="-127">
    <p>Hasta la vista, baby.</p>
  </elem005>
</root>

【讨论】:

  • 非常感谢您的回答。我很抱歉现在提供了一个例子。错误更正。我在底部编辑了我的初始帖子。我以前不知道 xs:assert 。我想我的问题的解决方案就是朝着这个方向。
猜你喜欢
  • 1970-01-01
  • 2016-06-20
  • 2018-04-05
  • 1970-01-01
  • 2018-09-24
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 2014-03-21
相关资源
最近更新 更多