如何遍历SimpleXML来编辑文本节点?

时间:2013-07-12 15:16:04

标签: php simplexml traversal

我需要使用SimpleXML实现以下算法:

  1. 将XML片段字符串放入SimpleXML对象中;
  2. 遍历所有节点,选择文本节点;
  3. 编辑文本节点(示例转换为大写);
  4. 将xml作为字符串返回。
  5. 问题:

    • 如何使用命名实体加载XML(例如 )。

    • 遍历XML以仅获取文本节点...使用$sx->xpath('//text()');我无法编辑节点,如何选择要编辑的文本节点?

1 个答案:

答案 0 :(得分:1)

您可以通过分配到$node[0]来覆盖SimpleXML XPath查询返回的节点的文本内容,例如

foreach ( $sx->xpath('//text()') as $text_node )
{
    $text_node[0] = 'Hello';
}

但是,请注意,SimpleXML本身并不真正具有文本节点的表示,因此如果元素中同时存在子元素和文本,则此类循环将表现得很奇怪。

例如,在给定XML <a><b>foo<c />bar</b><b>baz quux</b></a>的情况下,包含foobar的两个文本节点都将在SimpleXML中由第一个<b>元素表示,整个内容它将被'Hello'替换为两次,如下所示(live demo here)。在替换文本中使用计数器变量,我们可以清楚地看到发生了什么 - 所需的输出为<a><b>Hello 1<c />Hello 2</b><b>Hello 3</b></a>,但实际结果为<a><b>Hello 2</b><b>Hello 3</b></a>

$sx = simplexml_load_string('<a><b>foo<c />bar</b><b>baz quux</b></a>');

$counter = 1;
foreach ( $sx->xpath('//text()') as $text_node )
{
     $text_node[0] = 'Hello ' . $counter++;
}

echo $sx->asXML();

这种操作,至少在你解决问题时(找到文本节点,而不是迭代,可能递归地,在一组特定元素上),更适合于DOM API而不是SimpleXML。请记住,两者之间没有性能差异(它们都是同一XML解析器的包装器),并且您可以使用simplexml_import_dom()和{{3}在同一文档上使用两个API组合操作再次没有额外的开销,因为文档不需要重新解析。

以上是使用SimpleXML和DOM(dom_import_simplexml())混合修复的上述示例。如果这是整个代码,您可以直接使用DOM进行解析,但这表明如果您已经有其他代码使用SimpleXML操作此文档,它们是多么容易混合。请注意,最后,我们使用原始SimpleXML对象输出XML - 我们不需要运行simplexml_import_dom($dom),因为两个对象都引用内存中相同的已解析“文档”。

$sx = simplexml_load_string('<a><b>foo<c />bar</b><b>baz quux</b></a>');
$dom = dom_import_simplexml($sx);

$counter = 1;
$xpath = new DOMXpath($dom->ownerDocument);
foreach ( $xpath->query('//text()') as $text_node )
{
     $text_node->nodeValue = 'Hello ' . $counter++;
}

echo $sx->asXML();