PHP DOMDocument saveXML如何保留XML原标签闭合格式?
解决DOMDocument无法保留XML原标签闭合样式的问题
首先明确:PHP的DOMDocument没办法直接保留原始XML中空标签的两种闭合样式。因为XML规范里,<tag/>和<tag></tag>语义完全等价,DOM在解析时会把所有空标签统一处理,丢失了原始的语法格式信息,所以saveXML只能二选一输出——要么全自闭合,要么全成对闭合。
要实现保留原样式,得换思路,这里给两个可行方案:
方案一:用XMLReader手动构建输出
XMLReader是基于事件流的解析器,能读取到原始标签的闭合方式(通过isEmptyElement属性判断是不是自闭合标签)。我们可以遍历整个XML文档,逐个节点输出,遇到空标签时根据原始的闭合方式来决定输出格式。
示例代码:
$xml = <<<XML <example> <selfClosed/> <empty></empty> <text>content</text> </example> XML; $reader = new XMLReader(); $reader->xml($xml); $output = ''; while ($reader->read()) { switch ($reader->nodeType) { case XMLReader::ELEMENT: $output .= '<' . $reader->name; // 处理节点属性 while ($reader->moveToNextAttribute()) { $output .= ' ' . $reader->name . '="' . htmlspecialchars($reader->value) . '"'; } $reader->moveToElement(); if ($reader->isEmptyElement) { $output .= '/>'; } else { $output .= '>'; } break; case XMLReader::END_ELEMENT: $output .= '</' . $reader->name . '>'; break; case XMLReader::TEXT: case XMLReader::CDATA: $output .= $reader->value; break; // 处理注释、处理指令等其他节点类型 case XMLReader::COMMENT: $output .= '<!--' . $reader->value . '-->'; break; case XMLReader::PI: $output .= '<?' . $reader->name . ' ' . $reader->value . '?>'; break; } } echo $output;
这段代码会完全保留原始的标签闭合样式,输出和输入一致。
方案二:给成对闭合的空标签添加隐形内容
如果不想写复杂的遍历逻辑,可以先预处理原始XML,给那些原本是成对闭合的空标签添加一个零宽空格(​)——这个字符不可见,不会影响XML的实际内容,但会让DOMDocument认为该标签不是空元素,保存时就会输出成对闭合的格式;而原本自闭合的标签则保持原样。
示例代码:
$example = <<<XML <example> <selfClosed/> <empty></empty> </example> XML; // 预处理:给成对闭合的空标签插入零宽空格 $processedXml = preg_replace('/<(\w+)><\/\1>/', '<$1>​</$1>', $example); $dom = new DOMDocument; $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->loadXML($processedXml); // 可选:输出后移除零宽空格,完全还原原始格式 $result = str_replace('​', '', $dom->saveXML()); echo $result;
需要注意的是,正则表达式的替换逻辑可能需要根据你的XML结构调整,比如标签包含命名空间时,要修改正则匹配规则。
内容的提问来源于stack exchange,提问作者Cristian Belli
相关产品推荐
相关产品推荐

