Странные вещи в использовании importNode, namespace и appendChild в PHP - PullRequest
0 голосов
/ 14 ноября 2018

Я обнаружил, что xml после importNode будет иметь префикс «по умолчанию».После некоторых экспериментов я обнаружил некоторые странные явления.Как объяснить результаты ниже: Код здесь:

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

1 Ответ

0 голосов
/ 14 ноября 2018

Вы удаляете определение пространства имен для используемого пространства имен. В зависимости от порядка вызовов выполняются различные ремонтные работы. Ожидаемый способ удалить / изменить пространство имен - это пересечь и воссоздать узлы.

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="..." отсутствует ни в одном пространстве имен, а определения пространств имен с префиксом, таким как 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"/>
...