在xsd中使用密钥的正确方法

Zac*_*ack 3 .net xsd

我正在为我正在研究的项目编写XSD架构.下面的模式是我从一个微软的例子中获取并略微修改的模式.

我正在尝试使用key和keyref为一组项目声明一个唯一键,然后在另一个部分中引用该键.

我无法让它长时间工作.我会编写架构并设置一个测试文档,该文档应该通过验证失败,因为(1)重复键和(2)引用不存在键但不断传递的refkeys.

经过一系列的修补和工作后,我得到了它的工作.所以我试图将其缩小到示例中的功能,但导致它在我原来的尝试中无效.

我正在验证使用.NET XmlDocument和XmlSchema.我会将测试验证代码粘贴在底部.

我的问题是,为什么关键的工作如果声明它在下面但是如果在评论中声明的那样不起作用?

XSD:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
      targetNamespace="namespace1"
      xmlns="namespace1"
      xmlns:r="namespace1"
      elementFormDefault="qualified">  

<xs:element name="root">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="A" type="r:AType" maxOccurs="unbounded">
        <xs:keyref name="dummy" refer="r:pNumKey">
          <xs:selector xpath="part"/>
          <xs:field xpath="@ref-number"/>
        </xs:keyref>
      </xs:element>
      <xs:element name="B" type="r:BType"/>
    </xs:sequence>
  </xs:complexType>

  <!-- This works. -->
  <xs:key name="pNumKey">
    <xs:selector xpath="r:B/r:part"/>
    <xs:field xpath="@key-number"/>
  </xs:key>
  <!--
  This doesn't work.

  <xs:key name="pNumKey">
    <xs:selector xpath="B/part"/>
    <xs:field xpath="@key-number"/>
  </xs:key>
  -->
</xs:element>

<xs:complexType name="AType">
  <xs:sequence>
    <xs:element name="part" maxOccurs="unbounded">
      <xs:complexType>
        <xs:simpleContent>
          <xs:extension base="xs:string">
            <xs:attribute name="ref-number" type="xs:integer"/>
          </xs:extension>
        </xs:simpleContent>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="BType">
  <xs:sequence>
    <xs:element name="part" maxOccurs="unbounded">
      <xs:complexType>
        <xs:attribute name="key-number" type="xs:integer"/>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>
</xs:schema>
Run Code Online (Sandbox Code Playgroud)

验证码:

    private static void ValidateXml(string root, string xsdFileName, string xmlFileName)
    {
        ValidationEventHandler veh = new ValidationEventHandler(Program_ValidationEventHandler);
        XmlSchema schema = XmlSchema.Read(new XmlTextReader(root + xsdFileName), veh);

        XmlDocument xdoc = new XmlDocument();
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.Schemas.Add(schema);

        settings.ValidationType = ValidationType.Schema;

        settings.ValidationEventHandler +=
            new ValidationEventHandler(Program_ValidationEventHandler);

        XmlReader reader = XmlReader.Create(root + xmlFileName, settings);

        xdoc.Load(reader);

        Console.WriteLine(xdoc.SchemaInfo.Validity);
    }

    private static void Program_ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        Console.WriteLine(string.Format("-Message:{0}", e.Message));
    }
Run Code Online (Sandbox Code Playgroud)

Jaa*_*koK 7

我不是百分百肯定,但我非常自信地猜测selector元素中的XPath表达式并不关心目标命名空间,只关注您使用xmlns属性声明的命名空间.因此,由于您尚未声明默认命名空间,因此它会尝试确保part无命名空间中的元素是唯一的.由于part文档中的元素似乎位于namespace1命名part空间中,因此没有任何名称空间中的元素,因此这种唯一性约束会成功.

您可以通过向schema元素添加属性来验证我的猜测xmlns="namespace1".(由于架构文档中的所有元素都有前缀,因此这是您唯一需要做的事情.)如果您的注释掉的key元素有效,那么这似乎是正确的解释.

编辑:好的,我找到了答案.事实证明,在XPath 1.0中,当您拥有一个非前缀名称时,它会被自动解释为没有名称空间.无法在XPath 1.0中设置默认命名空间,因此当它们位于某个命名空间中时,您始终需要在XPath表达式中为所有名称添加前缀.因此,无论您是否在架构文档中声明了默认命名空间,注释掉的key定义都会尝试匹配没有命名空间的名称,这与您的文档不匹配.