0

我有以下结构的xml:

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

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

4

1 回答 1

2

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

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

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

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

我将元素名称写为扩展名称,约定将名称空间 URI 放在尖括号中。

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

由于该xmlns属性仅反映元素本身的命名空间 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 的功能。但是希望到目前为止,通过此答案中提供的信息,可以更清楚地说明为什么如果该属性值已经存在,则无法更改该属性值。

基于 libxml 的 PHP 扩展中基于 expat 的解析器确实允许“更改”现有属性值,无论它是否是xmlns*属性 - 因为它只是解析数据,您可以使用它动态处理它。

一个工作示例是:

$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:olduri:new

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

如本例所示,您在 XML 中使用的每个 XML 特性都需要由解析器处理。例如,缺少 XML 声明。但是,可以通过实现缺少的处理程序类回溯(例如,对于 CDATA 节)或通过输出缺少的输出(例如,对于“缺少的”XML 声明)来添加这些。我希望这会有所帮助,并向您展示如何更改这些不打算更改的值的替代方法。

于 2013-03-04T01:30:13.820 回答