PHP在XML文件中更改xmlns

时间:2013-03-03 17:49:59

标签: php xml xml-namespaces

我的xml具有以下结构:

<?xml version="1.0"?>
<ONIXMessage xmlns="http://test.com/test">
 ...data...
</ONIXMessage>

我需要使用自己的值更改xmlns属性。我该怎么做?最好使用DOMDocument类。

1 个答案:

答案 0 :(得分:2)

  

我需要使用自己的值更改xmlns属性。我该怎么做?最好是DOMDocument类。

这种设计是不可能的。每个DOMDocument都有一个根/文档元素。

在您的示例XML中,根元素是:

{http://test.com/test}ONIXMessage

我将元素名称编写为扩展名称,其惯例是将名称空间URI放在尖括号中。

在显示整个扩展名称的表单中编写元素名称也表明您不仅要在此处更改属性的值,而且还要更改名称空间的URI一个特定的元素。所以你想要改变元素名称。如果子节点位于同一名称空间中,它也可能包含它的任何子元素名称。

由于xmlns attribute仅反映元素本身的名称空间URI,因此无法更改它。在DOMDocument中设置后,您无法更改它。

您可以替换整个元素,但子项的命名空间也不会更改。这里有一个类似于你的XML的例子,只有textnode子节点(没有命名空间):

$xml = <<<EOD
<?xml version="1.0"?>
<ONIXMessage xmlns="uri:old">
 ...data...
</ONIXMessage>
EOD;

$doc = new DOMDocument();
$doc->loadXML($xml);
$newNode = $doc->createElementNS('uri:new', $doc->documentElement->tagName);
$oldNode = $doc->replaceChild($newNode, $doc->documentElement);

foreach(iterator_to_array($oldNode->childNodes, true) as $child) {
    $doc->documentElement->appendChild($child);
}

产生的XML输出是:

<?xml version="1.0"?>
<ONIXMessage xmlns="uri:new">
 ...data...
</ONIXMessage>

现在将输入XML更改为包含

等子项的内容
<?xml version="1.0"?>
<ONIXMessage xmlns="uri:old">
   <data>
     ...data...
   </data>
</ONIXMessage>

然后将创建以下输出,记下现在再次弹出的旧命名空间URI:

<?xml version="1.0"?>
<ONIXMessage xmlns="uri:new">
   <default:data xmlns:default="uri:old">
     ...data...
   </default:data>
</ONIXMessage>

正如您所看到的,DOMDocument没有提供替换现有元素的命名空间URI的功能。但希望到目前为止,通过此答案中提供的信息,更清楚的是,如果属性值已经存在,为什么不能更改该属性值。

expat based parser in the libxml based PHP extension允许“更改”现有属性值,无论它是否为xmlns*属性 - 因为它只是解析数据,您可以随时处理它。< / p>

一个工作示例是:

$xml = <<<EOD
<?xml version="1.0" encoding="utf-8"?>
<ONIXMessage xmlns="uri:old">
  <data>
    ...data...
  </data>
</ONIXMessage>
EOD;

$uriReplace = [
    'uri:old' => 'uri:new',
];

$parser = xml_parser_create('UTF-8');
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_set_default_handler($parser, function ($parser, $data) {
    echo $data;
});
xml_set_element_handler($parser, function ($parser, $name, $attribs) use ($xml, $uriReplace) {
    $selfClosing = '/>' === substr($xml, xml_get_current_byte_index($parser), 2);
    echo '<', $name;
    foreach ($attribs as $name => $value) {
        if (substr($name, 0, 5) === 'xmlns' && isset($uriReplace[$value])) {
            $value = $uriReplace[$value];
        }
        printf(' %s="%s"', $name, htmlspecialchars($value, ENT_COMPAT | ENT_XML1));
    }
    echo $selfClosing ? '/>' : '>';
}, function ($parser, $name) use ($xml) {
    $selfClosing = '/>' === substr($xml, xml_get_current_byte_index($parser) - 2, 2);
    if ($selfClosing) return;
    echo '</', $name, '>';
});

xml_parse($parser, $xml, true);
xml_parser_free($parser);

然后输出透明地将名称空间URI从uri:old更改为uri:new

<ONIXMessage xmlns="uri:new">
   <data>
     ...data...
   </data>
</ONIXMessage>

如此示例所示,您在XML中使用的每个XML功能都需要使用解析器进行处理。例如,缺少XML声明。但是,可以通过实现缺少的处理程序类(例如for CDATA sections)或输出缺失的输出(例如,“缺少的”XML声明)来添加这些。我希望这是有用的,并向您展示如何更改这些不打算更改的值的替代方法。