为什么 DomDocument#schemaValidate()会对有效的XML发出警告?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么 DomDocument#schemaValidate()会对有效的XML发出警告?相关的知识,希望对你有一定的参考价值。

我在php中构建了一个 DomDocument,我想根据XSD文件进行验证。我已经检查了更多的在线XML-XSD验证器,我的XML通过了所有这些验证。我究竟做错了什么?为什么我的XML传递其他验证器,但在DomDocument本身调用schemaValidate时却没有?

这是生成XML的PHP​​代码:

    $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>

我想要验证XML:

<?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'定义应该适用于所有后代元素,直到另外说明...所以我解决了它,但我仍然好奇,如果有人可以解释这种行为?

答案

名称空间感知方法(如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
";
foreach ($document->documentElement->childNodes as $bar) {
    echo '{'.$bar->namespaceURI.'}'.$bar->localName, "
";
}

输出:

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

使用非命名空间感知方法,您可以创建一个将序列化为相同XML字符串的DOM - 即使该节点是在没有命名空间的情况下创建的。

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
";
foreach ($document->documentElement->childNodes as $bar) {
    echo '{'.$bar->namespaceURI.'}'.$bar->localName, "
";
}

输出:

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

如果您使用名称空间创建XML,我建议使用名称空间感知方法。至于为什么节点不能继承父节点的名称空间。它存在于此之前(创建之后),只有在您追加/插入它时才会获得父级。

以上是关于为什么 DomDocument#schemaValidate()会对有效的XML发出警告?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 DOMDocument 会使网站加载速度变慢?

DOMDocument 命名空间有啥用?

PHP 4 中的新 DOMDocument()

我如何告诉 DOMDocument->load() 我希望它使用啥编码?

使用 DOMDocument,是不是可以获取某个 DOM 中存在的所有元素?

DOMDocument::loadHTML 错误