【问题标题】:Strange things in using importNode, namespace and appendChild in PHP在 PHP 中使用 importNode、命名空间和 appendChild 的奇怪事情
【发布时间】:2019-04-17 00:06:00
【问题描述】:

我发现importNode之后的xml会有一个“default”前缀。经过一些实验,我发现了一些奇怪的现象。 如何解释以下结果: 代码在这里:

<?php
$dom = new DOMDocument();
$xml = <<<XML                                                                                                                                  
<?xml version="1.0" encoding="UTF-8"?>                                                                                                         
<A xmlns="xmlns://www.abc.com"><B/></A>                                                                                                        
XML;
$dom->loadXML($xml);
$new_dom = new DOMDocument();
$new_root = $new_dom->createElement("root");
$new_node = $new_dom->importNode($dom->documentElement->childNodes->item(0), TRUE);

//order is important                                                                                                                           
$new_dom->appendChild($new_root);                         //1                                                                                  
$new_root->appendChild($new_node);                        //2                                                                                  
$new_node->removeAttributeNS("xmlns://www.abc.com", "");  //3                                                                                  
//                                                                                                                                             

echo $new_dom->saveXML()
?>

123
<?xml version="1.0"?>
<root><B/></root>

132,231,321,312
<?xml version="1.0"?>
<root><default:B/></root>

213
<?xml version="1.0"?>
<root xmlns:default="xmlns://www.abc.com"><default:B/></root>

【问题讨论】:

    标签: php xml dom namespaces


    【解决方案1】:

    您正在删除已用命名空间的命名空间定义。根据调用的顺序,会触发不同的修复/回退。删除/更改命名空间的预期方法是遍历并重新创建节点。

    libxml 为命名空间定义做了一些优化和回退。如果您做“奇怪”的事情,它可能会替换命名空间别名,并且可能会破坏结果。触发器是具有命名空间的属性。但没有前缀。与元素不同,命名空间内的属性必须有前缀。下面是一个触发默认命名空间前缀的例子:

    $document = new DOMDocument();
    $document->appendChild($document->createElementNS('urn:1', 'foo'));
    // add attribute in namespace without prefix
    $document->documentElement->setAttributeNS('urn:1', 'bar', 21);
    echo $document->saveXML();
    

    输出:

    <?xml version="1.0"?> 
    <foo xmlns="urn:1" xmlns:default="urn:1" default:bar="21"/>
    

    即使您为属性使用前缀,libxml 也会识别它与尝试优化的默认元素命名空间相同的命名空间:

    $document = new DOMDocument();
    $document->appendChild($document->createElementNS('urn:1', 'foo'));
    // add attribute in element namespace with prefix
    $document->documentElement->setAttributeNS('urn:1', 'b:bar', 21);
    echo $document->saveXML();
    

    如果我需要在命名空间内添加属性,我的规则是始终为命名空间使用前缀。

    $document = new DOMDocument();
    $document->appendChild($document->createElementNS('urn:1', 'b:foo'));
    $document->documentElement->setAttributeNS('urn:1', 'b:bar', 21);
    echo $document->saveXML();
    

    输出:

    <?xml version="1.0"?> 
    <b:foo xmlns:b="urn:1" b:bar="21"/>
    

    $new_node-&gt;removeAttributeNS("xmlns://www.abc.com", ""); 告诉 DOM 删除命名空间 xmlns://www.abc.com 内的一个没有名称的属性。但是xmlns="" 不是属性,而是命名空间定义。它使用相同的语法,并且在某些情况下可以使用属性方法进行修改,但 libxml 可能会再次添加它,因为后代或属性节点位于命名空间内并且需要定义。

    将它们视为属性xmlns="..." 不在任何命名空间中,并且带有前缀xmlns:foo="..." 的命名空间定义在保留的命名空间http://www.w3.org/2000/xmlns/ 中。 imho 删除命名空间定义的正确调用是:

    // xmlns="..."
    $node->removeAttributeNS(NULL, 'xmlns');
    // xmlns:prefix="..." - broken in PHP or libxml?
    $node->removeAttributeNS('http://www.w3.org/2000/xmlns/', 'prefix');
    

    例子:

    $document = new DOMDocument();
    $document->appendChild($document->createElementNS('urn:1', 'b:foo'));
    $document->documentElement->setAttributeNS(NULL, 'xmlns', 'urn:2');
    $document->documentElement->setAttributeNS(
      'http://www.w3.org/2000/xmlns/', 'xmlns:bar', 'urn:3'
    );
    echo $document->saveXML();
    $document->documentElement->removeAttributeNS(NULL, 'xmlns');
    echo $document->saveXML();
    // for some reason that does not work
    $document->documentElement->removeAttributeNS(
      'http://www.w3.org/2000/xmlns/', 'bar'
    );
    echo $document->saveXML();
    // but this works - weird
    $document->documentElement->removeAttributeNS('urn:3', 'bar');
    echo $document->saveXML();
    

    输出:

    <?xml version="1.0"?> 
    <b:foo xmlns:b="urn:1" xmlns:bar="urn:3" xmlns="urn:2"/> 
    <?xml version="1.0"?> 
    <b:foo xmlns:b="urn:1" xmlns:bar="urn:3"/> 
    <?xml version="1.0"?> 
    <b:foo xmlns:b="urn:1" xmlns:bar="urn:3"/> 
    <?xml version="1.0"?> 
    <b:foo xmlns:b="urn:1"/>
    

    【讨论】:

      猜你喜欢
      • 2017-05-10
      • 2012-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-29
      • 2011-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多