Использование проверки схемы XSD для запросов XPath - PullRequest
0 голосов
/ 24 сентября 2019

Я использую следующий код для создания 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, он выдаст осмысленное исключение.

Теперь я хочу получить информацию из DOMDocument, используя Xpath.Он также отлично работает, но с этого момента DOMDocument полностью отсоединен от XSD!Например, если у меня есть DOMNode , я не могу знать, относится ли он к типу simpleType или к типу complexType .Я могу проверить, есть ли у узла дочерние ( hasChild () ) узлы, но это не одно и то же.Кроме того, в XSD содержится больше информации (например, минимальное и максимальное количество вхождений и т. Д.).

Вопрос на самом деле в том, должен ли я сам запросить XSD или существует программный способзадавать такие вопросы.Т.е. это DOMNode сложный или простой тип?

В другом посте было предложено "обработать схему с использованием реального процессора схем, а затем использовать ее APIзадавать вопросы о содержании схемы ".Как бы вы сделали это с DOMDocument?

Для записи, оригинальный вопрос

Теперь я хотел бы приступить к анализу информации из DOMDocument с использованием XPath.Чтобы повысить целостность моих данных, я храню их в базе данных и даю клиенту осмысленное сообщение об ошибке. Я хотел постоянно использовать информацию схемы для проверки запросов.Т.е. я хотел проверить извлеченные дочерние узлы в отношении разрешенных дочерних узлов, определенных в xsd.Я хотел сделать это с помощью XPath в документе xsd.

Однако я перебрал этот пост .По сути, это говорит о том, что это какой-то изворотливый способ, и вы должны использовать реальный процессор схемы и использовать его API для выполнения запросов.Если я правильно понимаю, я использую настоящий процессор схемы с schemaValidate, но что подразумевается под использованием его API?

Я уже догадывался, что не правильно использую схему, но я понятия не имею, как исследовать правильное использование.

Вопрос

Если я использую schemaValidate в DOMDocument, это одноразовая проверка (true или false) или она связана с DOMDocument дольше, чем?Точно, могу ли я использовать проверку также для добавления узлов каким-либо образом или я могу использовать ее для выбора узлов, которые меня интересуют, как предложено в упомянутом сообщении SO?

Обновить

Вопрос был оцененнеясно, поэтому я хочу попробовать еще раз.Скажем, я хотел бы добавить узел или изменить значение узла.Могу ли я использовать схему, представленную в xsd, чтобы я мог проверить вводимые пользователем данные?Первоначально для этого я хотел вручную запросить xsd с другим экземпляром XPath, чтобы получить спецификации для определенного узла.Но, как предлагается в связанной статье, это не лучшая практика.Таким образом, вопрос заключается в том, предлагает ли библиотека DOM какой-либо API для такой проверки?

Может быть, я думаю об этом.Может быть, я просто добавляю узел и снова запускаю проверку и вижу, где / почему он ломается?В этом случае ответ пользовательской обработки ошибок будет правильным.Вы можете подтвердить?

1 Ответ

1 голос
/ 25 сентября 2019

Ваш вопрос не очень понятен, но, похоже, вы хотите получать подробные отчеты о любых ошибках проверки схемы.Хотя DomDocument::validateSchema() возвращает только логическое значение , вы можете использовать внутренние функции libxml для получения более подробной информации.

Мы можем начать с вашего исходного кода, изменив только одну вещь вtop:

<?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->getXmlErrors():

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

Для функции formatXmlError я только что скопировал пример из документации PHP , чторазбирает ошибку на что-то удобочитаемое человеком, но нет причины, по которой вы не могли бы вернуть некоторые структурированные данные или что-либо еще.

...