简体   繁体   中英

Why does \DomDocument#schemaValidate() give warning for valid XML?

I have built a \\DomDocument in PHP which I'd like to validate against an XSD file. I've checked in more online XML-XSD validator and my XML passed validation on all of them. What am I doing wrong? Why my XML passes other validators but not when calling schemaValidate on the DomDocument itself?

This is the piece of PHP code that generates the 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>

The error I get:

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

UPDATE:

I figured out, that the warning can be fixed if I use $xmlDoc->createElementNS and pass the namespace individually everywhere, instead of using $xmlDoc->createElement . However the output of the both is the same XML string. However the xmlns definition of the 'allatok' element should apply to all descendant elements until stated else... so I solved it, but I'm still curious if someon could explain this behavior?

The namespace aware methods (like DOMDocument::createElementNS() ) create an node in the provided namespace. They set the namespaceURI property, the other methods do not.

So the node is not in the expected namespace and the XML does not match the schema. Here is a small demo:

$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";
}

Output:

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

Using the non namespace aware methods you can create an DOM that will serialize to the same XML string - even if the node was created without namespaces.

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

Output:

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

Now if that document is loaded the parser will resolve the namespaces and both bar nodes are in the expected namespace.

$document->loadXML($xml);

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

Output:

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

I suggest using the namespace aware methods if you create an XML with namespaces. As for why the node can not inherit the namespace of the parent. It exists before that (after the create) and only gets a parent if you append/insert it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM