4
votes

When trying to validate a PHP DOMDocument object against a schema by using schemaValidate method, the next warning is being generated:

Warning: DOMDocument::schemaValidate(): Element 'foo': This element is not expected. Expected is ( {http://www.example.com}foo ). in X on line Y

It only happens with elements that have been appended to the DOMDocument. I prepared the next code snippet and schema so that anybody can test instantly:

Snippet:

$template = '
    <root
        xmlns="http://www.example.com"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.example.com schema.xsd"
    >
        <bar/>
    </root>
';

$DD = new DOMDocument(); 
$DD -> loadXML($template);
$foo = $DD -> createElement('foo');
$DD -> getElementsByTagName('root') -> item(0) -> appendChild($foo);
var_dump(htmlentities($DD -> saveXML()));
var_dump($DD -> schemaValidate(__DIR__ . '/schema.xsd'));

Schema:

<?xml version="1.0"?>
<xs:schema
    targetNamespace="http://www.example.com"
    xmlns:SiiDte="http://www.example.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified"
>
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="bar"/>
                <xs:element name="foo"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

I don't see the difference between foo and bar besides foo was added with appendChild method whilst bar added with loadXML method.

The validation is returning false (which means validation error). When loading foo with loadXML method, the error stop happenning, but it is definitely not the solution, because an XML need to be created dynamicaly in much cases.

¿Why appending element produces this validation error and how to solve?

1

1 Answers

1
votes

The element <foo> that you create is "missing" the namespace and therefore in the null-namespace.

The namespace is also the part you see in the curly (or angle) brackets in the error message:

  {http://www.example.com}foo
  `----------------------´`-´
           namespace      name

Instead of createElement use createElementNS and provide the namespace next to the element name.

As you save your created document as XML (to verify it manually for example by looking at it) you're totally right that the element seems to be like <bar>:

<?xml version="1.0"?>
<root xmlns="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com schema.xsd">
    <bar/>
<foo/></root>

But it has just be added there with it's null namspace (hence inserted with not much namespace things around) and in memory the element still has no namespace - and the validation is in memory.

Here is a full example with the validation in action:

<?php
/**
 * Element 'foo': This element is not expected. Expected is ( {http://www.example.com}foo )
 *
 * @link http://stackoverflow.com/a/29925747/367456
 */

$template = <<<XML
<root
    xmlns="http://www.example.com"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.example.com schema.xsd">
    <bar/>
</root>
XML;

$schema = <<<XML
<?xml version="1.0"?>
<xs:schema
    targetNamespace="http://www.example.com"
    xmlns:SiiDte="http://www.example.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="bar"/>
                <xs:element name="foo"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
XML;
$schema = 'data://text/xml;base64,' . base64_encode($schema);

$namespace = 'http://www.example.com';

$doc = new DOMDocument();
$doc->loadXML($template);
$foo = $doc->createElementNS($namespace, 'foo');
$doc->documentElement->appendChild($foo);
echo $doc->saveXML();

var_dump($doc->schemaValidate($schema));

Output is:

<?xml version="1.0"?>
<root xmlns="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com schema.xsd">
    <bar/>
<foo/></root>
bool(true)