【问题标题】:Using XSD schema validation for XPath queries对 XPath 查询使用 XSD 模式验证
【发布时间】:2019-09-24 11:27:29
【问题描述】:

我正在使用以下代码创建一个 DOMDocument 并根据外部 xsd 文件对其进行验证。

<?php

$xmlPath = "/xml/some/file.xml";
$xsdPath = "/xsd/some/schema.xsd";
    
$doc = new \DOMDocument();
$doc->loadXML(file_get_contents($xmlPath), LIBXML_NOBLANKS);

if (!$doc>schemaValidate($xsdPath)) {
    throw new InvalidXmlFileException();
}

更新 2(重写问题)

这很好用,这意味着如果 XML 与 XSD 的定义不匹配,它将抛出一个有意义的异常。

现在,我想使用 Xpath 从 DOMDocument 中检索信息。它也可以正常工作,但是,从这一点开始,DOMDocument 与 XSD 完全分离!例如,如果我有一个DOMNode,我不知道它是simpleType 类型还是complexType 类型。我可以检查该节点是否有子 (hasChild()) 节点,但这不一样。此外,XSD 中还有大量信息(例如,最小和最大出现次数等)。

问题真的是,我是否必须自己查询 XSD,或者是否有编程方式来询问这类问题。 IE。 DOMNode 是复杂类型还是简单类型?

another post 中建议“使用真正的模式处理器处理模式,然后使用它的 API 来询问有关模式内容的问题”。 XPath 是否有 API 来检索 XSD 的信息,或者是否有与 DOMDocument 不同的便捷方式?

记录一下,原来的问题

现在,我想继续使用 XPath 解析来自 DOMDocument 的信息。为了提高我存储到数据库中的数据的完整性并向客户端提供有意义的错误消息,我希望不断使用模式信息来验证查询。 IE。我想根据 xsd 中定义的允许子节点验证获取的子节点。我想通过在 xsd 文档上使用 XPath 来实现。

但是,我偶然发现了this post。它基本上说这是你自己的一种古怪方式,你应该使用真正的模式处理器并使用它的 API 来进行查询。如果我理解正确的话,我正在使用带有schemaValidate 的真正模式处理器,但是使用它的 API 意味着什么?

我已经猜到我没有以正确的方式使用架构,但我不知道如何研究正确的用法。

问题

如果我在 DOMDocument 上使用 schemaValidate 是一次性验证(真或假)还是与 DOMDocument 绑定更长时间?准确地说,我是否可以使用验证也以某种方式添加节点,或者我可以使用它来选择我感兴趣的节点,正如引用的 SO 帖子所建议的那样?

更新

这个问题被评为不清楚,所以我想再试一次。假设我想添加一个节点或编辑一个节点值。我可以使用 xsd 中提供的模式来验证用户输入吗?最初,为了做到这一点,我想使用另一个 XPath 实例手动查询 xsd 以获取某个节点的规范。但正如链接文章中所建议的,这不是最佳实践。所以问题是,DOM 库是否提供任何 API 来进行此类验证?

也许我想多了。也许我只是添加节点并再次运行验证,看看它在哪里/为什么会中断?在这种情况下,自定义错误处理的答案将是正确的。你能确认一下吗?

【问题讨论】:

    标签: php xml xsd domdocument xml-validation


    【解决方案1】:

    您的问题不是很清楚,但听起来您想获得有关任何架构验证失败的详细报告。而DomDocument::validateSchema()only returns a boolean,您可以使用内部libxml函数来获取更详细的信息。

    我们可以从您的原始代码开始,只更改顶部的一件事:

    <?php
    // without this, errors are echoed directly to screen and/or log
    libxml_use_internal_errors(true);
    $xmlPath = "file.xml";
    $xsdPath = "schema.xsd";
    
    $doc = new \DOMDocument();
    $doc->loadXML(file_get_contents($xmlPath), LIBXML_NOBLANKS);
    
    if (!$doc->schemaValidate($xsdPath)) {
        throw new InvalidXmlFileException();
    }
    

    然后我们可以在异常中发生有趣的事情,该异常可能(根据您提供的代码)在代码的更高位置捕获。

    <?php
    
    class InvalidXmlFileException extends \Exception
    {
        private $errors = [];
    
        public function __construct()
        {
            foreach (libxml_get_errors() as $err) {
                $this->errors[] = self::formatXmlError($err);
            }
            libxml_clear_errors();
        }
    
        /**
         * Return an array of error messages
         *
         * @return array
         */
        public function getXmlErrors(): array
        {
            return $this->errors;
        }
    
        /**
         * Return a human-readable error message from a libxml error object
         *
         * @return string
         */
        private static function formatXmlError(\LibXMLError $error): string
        {
            $return = "";
            switch ($error->level) {
            case \LIBXML_ERR_WARNING:
                $return .= "Warning $error->code: ";
                break;
             case \LIBXML_ERR_ERROR:
                $return .= "Error $error->code: ";
                break;
            case \LIBXML_ERR_FATAL:
                $return .= "Fatal Error $error->code: ";
                break;
            }
    
            $return .= trim($error->message) .
                   "\n  Line: $error->line" .
                   "\n  Column: $error->column";
    
            if ($error->file) {
                $return .= "\n  File: $error->file";
            }
    
            return $return;
        }
    }
    

    所以现在当你捕获你的异常时,你可以遍历$e-&gt;getXmlErrors()

    try {
        // do stuff
    } catch (InvalidXmlFileException $e) {
        foreach ($e->getXmlErrors() as $err) {
            echo "$err\n";
        }
    }
    

    对于formatXmlError 函数,我刚刚从the PHP documentation 复制了一个示例,该示例将错误解析为人类可读的内容,但没有理由您不能返回一些结构化数据或您喜欢的任何内容。

    【讨论】:

    • 谢谢,自定义错误处理也是我感兴趣的话题。但我原来的问题是不同的。我该如何帮助澄清?
    • 更新使事情变得更清晰(至少对我而言!)您希望在文档中添加任何内容之前对其进行验证。就个人而言,我不认为仅仅添加节点并重新验证整个事情有什么问题。 PHP 没有专用的模式解析器,但如果你想要一个,这看起来很有希望:github.com/goetas-webservices/xsd-reader/blob/master/README.md
    • 出现了更多问题,我重新编写了问题。我不想提出一个新问题,因为问题的根本原因仍然相同。我想请你审查它@miken32
    【解决方案2】:

    我认为您正在寻找的是 PSVI(后架构验证信息集),请参阅 this answer 获取一些参考资料。

    另一种选择是使用XPath2,它具有检查架构类型的运算符。

    我不知道 PHP 中是否有库可以让您获取 PSVI 或执行 XPath2 查询,在 Java 中有用于 PSVI 的 Xerces 和用于 XPath2 的 Saxon

    例如With Xerces is possible to cast a DOM Element to a Xerces ElementPSVI,用于获取元素的架构信息。

    我可以警告说,在架构上使用 XPath(正如您所做的那样)仅适用于简单的情况,因为架构的 XML 与实际的架构模型(组装架构)非常不同,后者是具有属性的组件图是从 XML 声明(模式文件)计算出来的,但规则非常复杂,几乎不可能用 XPath 重新创建。

    因此,您至少需要 PSVI 或进行 XPath2 查询,但根据我的经验,很难从 XML 模式中为应用程序用户获得适当的验证。

    你想达到什么目的?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-26
      • 2011-02-07
      • 2014-07-13
      • 2023-03-27
      • 2010-11-26
      • 1970-01-01
      • 2012-02-12
      相关资源
      最近更新 更多