Можно ли проверить XML на соответствие нескольким схемам в PHP?

Мне интересно, можно ли проверить xml на соответствие нескольким схемам в PHP или мне нужно каким-то образом объединить свои схемы.

Спасибо за ответ!


person Martin Vseticka    schedule 30.04.2010    source источник


Ответы (4)


Основной файл схемы должен содержать тег include для каждого дочернего файла схемы. Например:

<xs:include schemaLocation="2nd_schema_file.xsd"/>
person Francisco Luz    schedule 17.03.2014

Я решил свою проблему с помощью простого PHP-скрипта:

$mainSchemaFile = dirname(__FILE__) . "/main-schema.xml";
$additionalSchemaFile = 'second-schema.xml';


$additionalSchema = simplexml_load_file($additionalSchemaFile);
$additionalSchema->registerXPathNamespace("xs", "http://www.w3.org/2001/XMLSchema");
$nodes = $additionalSchema->xpath('/xs:schema/*');    

$xml = '';  
foreach ($nodes as $child) {
  $xml .= $child->asXML() . "\n";
}

$result = str_replace("</xs:schema>", $xml . "</xs:schema>", file_get_contents($mainSchemaFile));

var_dump($result); // merged schema in form XML (string)

Но это возможно только благодаря тому, что схемы одинаковые - т.е.

<xs:schema xmlns="NAMESPACE"
           targetNamespace="NAMESPACE"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified">

есть в обоих файлах.

person Martin Vseticka    schedule 30.04.2010

Разработчики Syfmony2 решили эту проблему. Это не чертовски чисто, но подойдет:

function validateSchema(\DOMDocument $dom)
{
    $tmpfiles = array();
    $imports = '';
    foreach ($this->schemaLocations as $namespace => $location) {
        $parts = explode('/', $location);
        if (preg_match('#^phar://#i', $location)) {
            $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
            if ($tmpfile) {
                file_put_contents($tmpfile, file_get_contents($location));
                $tmpfiles[] = $tmpfile;
                $parts = explode('/', str_replace('\\', '/', $tmpfile));
            }
        }
        $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
        $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));

        $imports .= sprintf('    <xsd:import namespace="%s" schemaLocation="%s" />' . PHP_EOL, $namespace, $location);
    }

    $source = <<<EOF
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns="http://symfony.com/schema"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://symfony.com/schema"
    elementFormDefault="qualified">

    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
$imports
</xsd:schema>
EOF
    ;

    $current = libxml_use_internal_errors(true);
    $valid = $dom->schemaValidateSource($source);
    foreach ($tmpfiles as $tmpfile) {
        @unlink($tmpfile);
    }
    if (!$valid) {
        throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
    }
    libxml_use_internal_errors($current);
}
person Alan Gabriel Bem    schedule 08.05.2011

Учитывая, что метод DOMDocument::schemaValidate получает путь к файлу схемы в качестве параметра, я бы сказал, что вам просто нужно вызвать этот метод несколько раз: один раз для каждой из ваших схем.

См. также DOMDocument::schemaValidateSource, если у вас есть схемы в строках PHP; идея (и ответ) будет той же: просто вызовите метод несколько раз.

person Pascal MARTIN    schedule 30.04.2010
comment
Это тоже было мое предположение, но оно, к сожалению, не работает. Думаю, мне придется объединить мои схемы. - person Martin Vseticka; 30.04.2010