Почему \DomDocument#schemaValidate() выдает предупреждение о допустимом XML?

Я создал \DomDocument в PHP, который я хотел бы проверить по файлу XSD. Я проверил больше онлайн-валидаторов XML-XSD, и мой XML прошел проверку на всех из них. Что я делаю неправильно? Почему мой XML проходит через другие валидаторы, но не при вызове schemaValidate в самом DomDocument?

Это фрагмент кода PHP, который генерирует XML:

    $xmlDoc = new \DOMDocument('1.0', 'UTF-8');
    $rootElem = $xmlDoc->createElementNS('http://fip.loginet.hu', 'allatok');

    /** @var RearingAnimal $animal */
    foreach($this->animals as $animal){
        $animalElem = $xmlDoc->createElement('allat');
        $animalElem->appendChild($xmlDoc->createElement('fulszam', $animal->getEarNumber()));
        $animalElem->appendChild($xmlDoc->createElement('tenyeszet', $animal->getRearingCode()));
        $animalElem->appendChild($xmlDoc->createElement('szuletesi_ido', $animal->getBirthDate()));
        $animalElem->appendChild($xmlDoc->createElement('fajta', $animal->getBreed()));
        $animalElem->appendChild($xmlDoc->createElement('ivar', $animal->getSex()));

        $rootElem->appendChild($animalElem);
    }

    $xmlDoc->appendChild($rootElem);

    if(!$xmlDoc->schemaValidate($this->getXsdFileName())){
        throw new \Exception("XML Socument validation failed!");
    }

XSD:

<schema 
     attributeFormDefault="unqualified" 
     elementFormDefault="qualified" 
     targetNamespace="http://fip.loginet.hu"                   
     xmlns="http://www.w3.org/2001/XMLSchema" 
     xmlns:tns="http://fip.loginet.hu">
<element name="allatok">
    <complexType>
        <sequence>
            <element name="allat" minOccurs="0" maxOccurs="unbounded" type="tns:Allat"/>
        </sequence>
    </complexType>
</element>
<complexType name="Allat">
    <sequence>
        <element name="fulszam" type="string"/>
        <element name="tenyeszet" type="integer"/>
        <element name="szuletesi_ido" type="date"/>
        <element name="fajta" type="integer"/>
        <element name="ivar" type="tns:Ivar"/>
    </sequence>
</complexType>
<simpleType name="Ivar">
    <restriction base="string">
        <enumeration value="m"/>
        <enumeration value="f"/>
    </restriction>
</simpleType>

And the XML i'd like to validate against:

<?xml version="1.0" encoding="UTF-8"?>
<allatok xmlns="http://fip.loginet.hu">
    <allat>
        <fulszam>HU 30966 0259 0</fulszam>
        <tenyeszet>4737016</tenyeszet>
        <szuletesi_ido>2016-09-03</szuletesi_ido>
        <fajta>1</fajta>
        <ivar>m</ivar>
    </allat>
    <allat>
        <fulszam>HU 31342 0375 1</fulszam>
        <tenyeszet>4737016</tenyeszet>
        <szuletesi_ido>2016-03-21</szuletesi_ido>
        <fajta>2</fajta>
        <ivar>m</ivar>
    </allat>
    <allat>
        <fulszam>HU 31342 4595 1</fulszam>
        <tenyeszet>4737016</tenyeszet>
        <szuletesi_ido>2016-03-21</szuletesi_ido>
        <fajta>2</fajta>
        <ivar>m</ivar>
    </allat>
</allatok>

Ошибка, которую я получаю:

Warning: DOMDocument::schemaValidate(): Element 'allat': This element is not expected. Expected is ( {http://fip.loginet.hu}allat )

ОБНОВИТЬ:

Я понял, что предупреждение можно исправить, если я использую $xmlDoc->createElementNS и передаю пространство имен везде индивидуально, вместо того, чтобы использовать $xmlDoc->createElement. Однако вывод обоих - одна и та же строка XML. Однако определение xmlns элемента 'allatok' должно применяться ко всем элементам-потомкам, пока не будет указано иное... поэтому я решил это, но мне все еще любопытно, может ли кто-нибудь объяснить это поведение?


person ACs    schedule 12.09.2018    source источник


Ответы (1)


Методы, учитывающие пространство имен (например, DOMDocument::createElementNS()), создают узел в предоставленном пространстве имен. Они устанавливают свойство namespaceURI, а другие методы — нет.

Таким образом, узел находится не в ожидаемом пространстве имен, а XML не соответствует схеме. Вот небольшая демонстрация:

$document = new \DOMDocument();
$foo = $document->appendChild($document->createElementNS('urn:foo', 'f:foo'));
$foo->appendChild($document->createElementNS('urn:foo', 'f:bar'));
$foo->appendChild($document->createElement('f:bar'));

echo "after create\n";
foreach ($document->documentElement->childNodes as $bar) {
    echo '{'.$bar->namespaceURI.'}'.$bar->localName, "\n";
}

Выход:

after create 
{urn:foo}bar 
{}f:bar 

Используя методы, не учитывающие пространство имен, вы можете создать модель DOM, которая будет сериализоваться в ту же строку XML, даже если узел был создан без пространств имен.

echo $xml = $document->saveXML();

Выход:

<?xml version="1.0"?> 
<f:foo xmlns:f="urn:foo"><f:bar/><f:bar/></f:foo>

Теперь, если этот документ загружен, синтаксический анализатор разрешает пространства имен, и оба узла bar находятся в ожидаемом пространстве имен.

$document->loadXML($xml);

echo "after load\n";
foreach ($document->documentElement->childNodes as $bar) {
    echo '{'.$bar->namespaceURI.'}'.$bar->localName, "\n";
}

Выход:

after load 
{urn:foo}bar 
{urn:foo}bar

Я предлагаю использовать методы, учитывающие пространство имен, если вы создаете XML с пространствами имен. Что касается того, почему узел не может наследовать пространство имен родителя. Он существует до этого (после создания) и получает родителя только в том случае, если вы добавляете/вставляете его.

person ThW    schedule 14.09.2018