使用SimpleXML保存XML时如何为所有节点重新添加CDATA?
问题
我通过以下PHP代码加载了一个较大的嵌套XML文件:
$content = simplexml_load_string( $xmlString, null, LIBXML_NOCDATA );
现在需要把XML导出/保存为字符串时,给所有节点重新添加CDATA转义。
我原本尝试了下面的方法:
$xmlIterator = new RecursiveIteratorIterator( new SimpleXMLIterator($xml_string), RecursiveIteratorIterator::SELF_FIRST ); foreach ($xmlIterator as $nodeName => &$node) { $node->textContent = sprintf('<![CDATA[%s]]>', $node->textContent); }
但这种方法无法通过引用直接更新节点,没法直接在XML树里添加CDATA内容。
解决方案
SimpleXML本身对CDATA的支持有限——它会自动将CDATA解析为普通文本,且无法直接创建CDATA节点。推荐转用DOMDocument处理,DOM可以直接操作CDATA节点,结合遍历逻辑就能实现需求:
方法1:SimpleXML转DOMDocument处理
先把SimpleXML对象转为DOM对象,再递归遍历所有节点,将文本内容替换为CDATA节点:
// 将SimpleXML对象转为DOMElement $dom = dom_import_simplexml($content)->ownerDocument; // 递归处理所有节点的文本内容 function replaceTextWithCDATA(\DOMNode $node) { if ($node->nodeType === XML_TEXT_NODE && trim($node->nodeValue) !== '') { $cdata = $node->ownerDocument->createCDATASection($node->nodeValue); $node->parentNode->replaceChild($cdata, $node); } elseif ($node->hasChildNodes()) { foreach (iterator_to_array($node->childNodes) as $child) { replaceTextWithCDATA($child); } } } replaceTextWithCDATA($dom->documentElement); // 导出为带CDATA的XML字符串 $xmlWithCDATA = $dom->saveXML();
方法2:直接用DOMDocument加载XML
如果不需要保留SimpleXML的操作逻辑,也可以直接用DOMDocument加载XML后处理:
$dom = new DOMDocument(); // 加载时遵循原逻辑忽略CDATA,直接解析为文本 $dom->loadXML($xmlString, LIBXML_NOCDATA); // 调用同上面的递归处理函数 replaceTextWithCDATA($dom->documentElement); $xmlWithCDATA = $dom->saveXML();
注意事项
- 处理时跳过空文本节点,避免生成多余的CDATA块
- 如果节点文本包含
]]>,需要先做转义(比如替换为]]]]><
