php - 在 PHP 中使用 importNode、namespace 和 appendChild 的奇怪事情

标签 php xml dom namespaces

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

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

最佳答案

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

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

将它们视为属性 xmlns="..."不在任何 namespace 和 namespace 定义中,前缀类似于 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"/>

关于php - 在 PHP 中使用 importNode、namespace 和 appendChild 的奇怪事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53293167/

相关文章:

php 回显其他 php 代码

php - 如何检测用户上传的文件是否大于 post_max_size?

xml - Schematron 条件验证

javascript - 仅使用 Javascript 读取 XML 文件

sql-server - 具有多个命名空间的 SQL Server Xml 查询

javascript - python Selenium : Remove certain characters from web page body

php - 在 PHP 中,如何在不使系统变慢的情况下记录事务处理?

php - initWithObjects Keys for JSON POST to MySQL via PHP - 在数据库中获取空行

javascript - Facebook:有比 Like 按钮事件更快的替代方案吗?

javascript - 保存具有 id 的元素的变量存储在哪里?