2

我有一个 XML 字符串和一个 Schema 加载并传递给一个函数。我让它正确地根据模式验证 XML,但是它总是在第一个无效元素的范围内停止验证。无效数据,它继续,无效/缺少属性,继续,但无效元素,它停止并且不会在该范围内进一步验证。

架构如下:

<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:b="http://schemas.microsoft.com/BizTalk/2003" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="items">
          <xs:complexType>
            <xs:sequence maxOccurs="unbounded">
              <xs:element name="foo">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="bar" type="xs:integer" />
                    <xs:element name="bat" type="xs:integer" />
                  </xs:sequence>
                  <xs:attribute name="attr1" type="xs:integer" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

xml如下:

<root>
  <items>
    <foo attr1='1'>
      <invalid0>1</invalid0>
      <invalid1>b</invalid1>
    </foo>
    <foo attr1='1'>
      <invalid2>b</invalid2>
      <bat>b</bat>
    </foo>
    <foo attr1='1'>
      <bar>3</bar>
    </foo>
    <invalidFoo attr1='1'>
      <bar>d</bar>
      <bat>2</bat>
    </invalidFoo>
    <foo>
      <bar>3</bar>
      <bat>q</bat>
    </foo>
  </items>
</root>

所以,在这个例子中发生了什么,验证器到达第一个<foo>并且看到<invalid0>并且没有在<foo>任何进一步验证,因此错过了<invalid1>. Validator 移动到下一个<foo>.

接下来<foo>它看到有一个<invalid2>不属于那里并且不费心去捕捉<bat>元素的无效数据(字符串而不是整数)。直接进入下一个<foo>

它进入下一个<foo>元素并抛出一个关于缺失的错误,<bat>然后继续下一个<foo>,很酷。

现在它到达了<invalidFoo>并且理所当然地,在里面没有做任何验证,<invalidFoo>因为当然,什么是<invalidFoo>

对我来说,症结在于,此时验证器停止验证所有以下<foo>同级元素,因此最后的无效数据<bat>不会被捕获。所以现在,我问的原因是因为我使用验证的方式是尝试捕获所有错误(或至少尽可能多)并将它们传递回用户。我在实际代码中所做的第一个测试与此等价:

<root>
  <items>
    <invalidFoo attr1='1'>
      <invalid0>1</invalid0>
      <invalid1>b</invalid1>
    </invalidFoo>
    <foo attr1='1'>
      <invalid2>b</invalid2>
      <bat>b</bat>
    </foo>
    <foo attr1='1'>
      <bar>3</bar>
    </foo>
    <invalidFoo attr1='1'>
      <bar>d</bar>
      <bat>2</bat>
    </invalidFoo>
    <foo>
      <bar>3</bar>
      <bat>q</bat>
    </foo>
  </items>
</root>

因此,验证者首先看到了这一点<invalidFoo>并停止了死亡。在很长一段时间里,我一直假设由于某种原因验证总是在第一个错误时停止。直到我添加了一个有效的<foo>返回,它才开始连续捕获和累积其他无效数据错误。但是一旦遇到无效的元素标记名,就会跳过所有同级/子级验证。它只发生在无效元素上,而不是属性或数据上。

现在,我不是说这是对还是错……我是在问这是对还是错?验证器是否应该继续运行,尤其是在兄弟元素的情况下?还是应该停止并根据前一个元素无效而基本上调用整个元素列表无效?在这种情况下 Xml Schema Validator 的预期行为是什么?

这一切都是使用以下 C# 代码完成的(按我的预期工作):

 public static void ValidateAgainstSchema(string XMLSourceDocument, XmlSchemaSet validatingSchemas)
 {
    if (validatingSchemas == null)
    {
        throw new ArgumentNullException("In ValidateAgainstSchema: No schema loaded.");
    }

    string errorHolder = string.Empty;
    ValidationHandler handler = new ValidationHandler();

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.CloseInput = true;
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += new ValidationEventHandler(handler.HandleValidationError);
    settings.Schemas.Add(validatingSchemas);
    settings.ValidationFlags =
        XmlSchemaValidationFlags.ReportValidationWarnings |
        XmlSchemaValidationFlags.ProcessIdentityConstraints |
        XmlSchemaValidationFlags.ProcessInlineSchema |
        XmlSchemaValidationFlags.ProcessSchemaLocation;

    StringReader srStringReader = new StringReader(XMLSourceDocument);

    using (XmlReader validatingReader = XmlReader.Create(srStringReader, settings))
    {
        while (validatingReader.Read()) { }
    }

    if (handler.MyValidationErrors.Count > 0)
    {
        foreach (String messageItem in handler.MyValidationErrors)
        {
            errorHolder += messageItem;
        }
        throw new XmlSchemaValidationException(errorHolder);
    }
}

验证事件处理程序只是捕获错误并将它们添加到IList<string>稍后一起显示。

4

1 回答 1

2

它通过遍历树来处理元素,所以一旦它得到一个不适合的节点,它就会丢失。然而,属性不是分层的,它们是一个列表,所以它是直接去/不去,它可以继续,类型检查也很简单。

您可以查看您的示例并认为它可以处理这个问题。

<root>
  <items>
    <invalidFoo attr1='1'>
      <invalid0>1</invalid0>
      <invalid1>b</invalid1>
      <foo attr1='1'>
        <bar>b</bar>
        <bat>b</bat>
      </foo>    
    </invalidFoo>
  <items>
<root>

是否应该将 foo 视为项目的子项。foo 真的是 foo 吗?

如果您想要一个真正的折头机,请想象xsd:choice在其中有一两个并选择不符合架构的有效节点。这是其中一种情况,尝试并继续是“危险的”,所以它提示并说你需要先解决这个问题,这样我才能明智地验证接下来会发生什么。

于 2012-11-09T22:31:46.363 回答