【问题标题】:Why does \DomDocument#schemaValidate() give warning for valid XML?为什么 \DomDocument#schemaValidate() 对有效 XML 发出警告?
【发布时间】:2018-09-12 20:15:24
【问题描述】:

我在 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-&gt;createElementNS 并在任何地方单独传递命名空间,而不是使用 $xmlDoc-&gt;createElement,则可以修复警告。然而,两者的输出是相同的 XML 字符串。然而,'allatok' 元素的 xmlns 定义应该适用于所有后代元素,除非另有说明......所以我解决了它,但我仍然很好奇是否有人可以解释这种行为?

【问题讨论】:

    标签: php xml xsd domdocument xsd-validation


    【解决方案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 
    

    使用非命名空间感知方法,您可以创建一个将序列化为相同 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\n";
    foreach ($document->documentElement->childNodes as $bar) {
        echo '{'.$bar->namespaceURI.'}'.$bar->localName, "\n";
    }
    

    输出:

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

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

    【讨论】:

      猜你喜欢
      • 2016-01-19
      • 1970-01-01
      • 1970-01-01
      • 2014-05-22
      • 1970-01-01
      • 1970-01-01
      • 2018-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多