【问题标题】:XSD - Fail Some, Pass SomeXSD - 失败一些,通过一些
【发布时间】:2013-02-23 02:10:26
【问题描述】:

我正在尝试做一些不可能的事情。

我有一个包含相同类型记录列表的 XML 文档。像这样的:

<root>
  <record>I'm a shark.</record>
  <record>I'm a shark.</record>
  <record>Suck it.</record>
  <record>I'm a shark.</record>
</root>

首先,我通过 XSD 运行它以确保标签正确。然后,我解组它并对实际值进行一些编程验证。我想通过&lt;xs:pattern /&gt; 将第二步折叠到 XSD 中(将值与正则表达式匹配以测试有效性)。

问题是我的业务规则是我应该继续处理 XML 文档中列出的所有有效记录,并且只处理无效的特定记录。在我上面的例子中,我想要让“Suck it”失败。重视并传递所有“我是鲨鱼”。值转发到某些处理步骤以供实际使用。

不幸的是,据我所知,在 XSD 中,如果某个部分失败,则整个文档只是“糟糕”并且无法通过验证。所以,在我上面的例子中,“Suck it”。 value 删除整个文档。有没有办法解决这个问题?我只是坚持我的第二个编程步骤吗?如果我 可以 仅使单个标签而不是整个文档失败,是否有某种方法可以解决“此标签因这个原因而失败”。验证期间?

编辑:我最终使用了 SAXParser 并在其上设置了 Schema,并将扩展 DefaultHandler 的自定义类交给它,该类在某种程度上手动处理 XML。

我在我的自定义 DefaultHandler 内部设置了一个私有类 Node,这是一个非常简单的 Tree 实现。每个Node 都包含一个开始标记、值和结束标记,所有这些都存储为Strings,以及与父级和子级的关系。每当我收到包含以“cvc-pattern-valid”或“cvc-type.3.1.3”(或我想要捕获的任何 XML 错误)开头的消息的 SAXException 时,我都会删除我所在的 Node从我正在建造的树的中间(因为它已经坏了),然后继续前进。然后,当我通过在根(使用各种 StringBuilders)上调用 Node.depthFirstSearch() 完成解析后,我可以将整个文档(减去剔除标签)输出为一个大 XML String

我现在的问题是,感觉就像我做了大量的工作只是为了将 XML 视为 XML。我必须重新添加“&lt;”、“&gt;”和“&lt;\”字符,因为DefaultHandler 中的方法只给了我像剥离的qName 这样的东西。所有这些树的构建和遍历似乎效率低下;喜欢太多的工作。肯定有更简单的方法吗?

注意:我之所以要将 XML 保留为 XML,是因为这是我以前的工作流程:

XSD -> XSLT -> Unmarshal to JAXB-Annotated Object

现在是这样的:

SAXParser(XSD) -> XSLT -> Unmarshal to JAXB-Annotated Object

也许有一些神奇的方法可以做到:

SAXParser(XSD, XSLT) -> Unmarshal to JAXB-Annotated Object

或者

SAXParser(XSD, XSLT, Unmarshal to JAXB-Annotated Object)

但我不知道那会是什么。

编辑:好吧,除了可能的低效率之外,扩展DefaultHandler,覆盖DefaultHandler.error(SAXParseException exception) 是正确的答案,至少对我来说是这样。因此,Petru 得到了梦寐以求的绿色复选标记。

【问题讨论】:

  • 你在哪个平台上?爪哇? .NET?
  • 我不知道有任何 xsd 验证器可以满足您的需求。这根本不是 XSD 的设计目的。不过,您可能会为此有效地使用 XSLT。只需使用选择性复制的复制模板即可。
  • @PetruGardea:Java。我主要使用 JAXB,在一些特殊情况下使用一点 Saxon。
  • @StanleyDeBoer:我不能只跳过无效的,我需要具体知道哪些失败,以便生成通知。

标签: xml validation xsd jaxb


【解决方案1】:

JAXB 能够设置custom error handler,因此允许您覆盖默认行为,即放弃所有处理。用您的特定测试用例尝试一下,看看它是如何工作的。

据我所知,所有主要的验证器都有一种基于事件的方法来允许对验证错误进行自定义处理,以便继续处理。

最坏的情况可能是实际解析您的 XML,例如,使用 SAX API(而不是直接使用 JAXB)它可以让您更好地控制处理错误;然后任何record 节点被成功验证,解组到您的 JAVA 类中(尽管您最终会进行双重解析)。

【讨论】:

    【解决方案2】:

    让我们区分概念层面和实践层面。

    从概念上讲 - XSD 不仅为整个文档(或更具体地说,为验证开始的文档节点)定义有效性,还为以验证开始的节点为根的子树中的每个节点定义有效性。

    因此,在文档的 PSVI(模式验证后信息集)中,您的每个“记录”元素在概念上都标有 [validity=valid] 或 [validity=invalid] 或 [validity=notKnown]。正如您所观察到的,无效性会向上传播,因此如果父“根”元素具有无效的子元素,它通常也是无效的。

    XSD 规范完全没有说明发现其输入无效的应用程序是否应该继续处理输入或中止;这超出了 XSD 规范的范围。如果您想处理有效的“记录”元素并对无效元素执行其他操作,XSD 规范旨在使验证器能够为您提供执行此操作所需的信息。

    实际上 - 许多用户和一些实现者还没有看到 XSD 定义的更丰富的有效性概念的实用性,因此许多验证器和代码绑定工具提供的 API 在默认情况下基本上将有效性降低到验证的单个布尔属性root,并且在某些情况下,这种简化的有效性概念是该工具实际支持的唯一概念。

    因此,您的验证器 API 可能会或可能不会让您访问“记录”元素的有效性信息。如果是这样,您只需要编写消费代码来询问 API 并采取相应措施。如果没有,您需要告诉您的供应商您需要该信息(或寻找不同的验证器)。如果您使用数据绑定工具来生成代码,则适用相同的原则:了解如何使您的工具生成可以在存在无效输入的情况下继续使用的代码,如果该工具不支持该功能,请咨询供应商为什么不呢。

    XSD 不会尝试为验证器定义 API 或最低质量标准,因此说服供应商提供对 XSD 完整原生有效性概念的访问权限的唯一方法是通过市场压力。

    XSD 与其他模式语言的不同之处在于,它不是将有效性定义为整个文档的简单布尔属性,而是定义为文档部分中每个节点的三值属性(有效、无效、未知)被验证。像您描述的那样的使用场景(与 Stanley de Boer 在评论中提供的观点相反)是这项工作的核心动机。因此,如果您不能使用当前的 XSD 验证器做您想做的事情,那么障碍在于实现,而不是 XSD 本身。

    祝你好运。

    【讨论】:

      【解决方案3】:

      Saxon 没有达到 Michael Sperberg-McQueen 所描述的理想化 XSD 处理器,主要是因为它主要是为满足模式感知 XSLT 和 XQuery 处理的需求而设计的,它们不需要 XSD 规范中内置的所有灵活性。但是,您仍然可以走很长一段路,因为您并不真的需要完整的 PSVI:您需要的是源文档加上一个错误列表 [我的术语,不是 XSD 规范中使用的那个],链接在一起某种方式。而且您可以很容易地做到这一点 - 详细信息取决于您想要基于树还是基于事件的处理方法。

      (几乎可以肯定,其他 XSD 处理器也是如此,但 Saxon 是我最了解的处理器)。

      如果您正确选择了处理选项,Saxon 将始终在出现验证错误后继续处理,但在某些情况下,它会决定不对文档的某些部分进行任何进一步的验证。例如,如果一个内容模型应该包含 (a,b,c,d) 而实例实际上包含 (a,x,b,c,d),那么一旦在 x 处报告错误,则不进行验证在 b、c 和 d 上。

      【讨论】:

        猜你喜欢
        • 2015-07-15
        • 1970-01-01
        • 2012-09-09
        • 2020-12-21
        • 2020-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多