在 PHP 中使用 DOMDocument 缩进
Posted
技术标签:
【中文标题】在 PHP 中使用 DOMDocument 缩进【英文标题】:Indentation with DOMDocument in PHP 【发布时间】:2010-10-19 06:37:02 【问题描述】:我正在使用DOMDocument
生成一个新的 XML 文件,我希望文件的输出能够很好地缩进,以便人类读者可以轻松理解。
比如DOMDocument
输出这个数据时:
<?xml version="1.0"?>
<this attr="that"><foo>lkjalksjdlakjdlkasd</foo><foo>lkjlkasjlkajklajslk</foo></this>
我希望 XML 文件是:
<?xml version="1.0"?>
<this attr="that">
<foo>lkjalksjdlakjdlkasd</foo>
<foo>lkjlkasjlkajklajslk</foo>
</this>
我一直在四处寻找答案,我发现的一切似乎都在试图以这种方式控制空白:
$foo = new DOMDocument();
$foo->preserveWhiteSpace = false;
$foo->formatOutput = true;
但这似乎没有任何作用。也许这仅在读取 XML 时有效?请记住,我正在尝试编写新文档。
DOMDocument
有内置的功能吗?或者有什么功能可以轻松完成?
【问题讨论】:
这里有一个很好的简单函数(基于正则表达式):Format XML with php 我不确定问题是什么。您显示的代码将给出您要求的输出。证明:codepad.org/4UGyRspx 和 codepad.org/bLTOFQrp - 您是否询问缩进级别,例如使用了多少个空格? 只要涉及缩进就相关:Converting indentation with preg_replace (no callback) 【参考方案1】:DomDocument 可以解决问题,我个人花了几个小时谷歌搜索并试图弄清楚这一点,我注意到如果你使用
$xmlDoc = new DOMDocument ();
$xmlDoc->loadXML ( $xml );
$xmlDoc->preserveWhiteSpace = false;
$xmlDoc->formatOutput = true;
$xmlDoc->save($xml_file);
按这个顺序,它只是不起作用,但是,如果你使用相同的代码但按这个顺序:
$xmlDoc = new DOMDocument ();
$xmlDoc->preserveWhiteSpace = false;
$xmlDoc->formatOutput = true;
$xmlDoc->loadXML ( $xml );
$xmlDoc->save($archivoxml);
就像一个魅力,希望这会有所帮助
【讨论】:
老兄!你摇滚!感谢您发现这一点! 该死...这似乎只适用于 XML,html 看起来仍然很丑。 =/ 我不知道你是如何体验到这一点的,因为即使在从 5.0.0 一直到 7.0.5 的所有 PHP 版本中,即使首先调用loadXML()
,格式也会起作用:3v4l.org/QrLlo
@Benjamin 只有在某些情况下,当loadXML()
首先运行时,它才能正确格式化。其他时候它将无法修复空格3v4l.org/Qt9EV
@MikeChelen 很好发现!【参考方案2】:
在 John 的一些帮助和我自己玩弄这个之后,似乎即使 DOMDocument 对格式化的固有支持也不能满足我的需求。所以,我决定编写自己的缩进函数。
这是一个非常粗略的函数,我只是快速拼凑而成,所以如果有人有任何优化技巧或关于它的总体看法,我会很高兴听到它!
function indent($text)
// Create new lines where necessary
$find = array('>', '</', "\n\n");
$replace = array(">\n", "\n</", "\n");
$text = str_replace($find, $replace, $text);
$text = trim($text); // for the \n that was added after the final tag
$text_array = explode("\n", $text);
$open_tags = 0;
foreach ($text_array AS $key => $line)
if (($key == 0) || ($key == 1)) // The first line shouldn't affect the indentation
$tabs = '';
else
for ($i = 1; $i <= $open_tags; $i++)
$tabs .= "\t";
if ($key != 0)
if ((strpos($line, '</') === false) && (strpos($line, '>') !== false))
$open_tags++;
else if ($open_tags > 0)
$open_tags--;
$new_array[] = $tabs . $line;
unset($tabs);
$indented_text = implode("\n", $new_array);
return $indented_text;
【讨论】:
简短说明:有 str_repeat() 用于创建选项卡。其余的功能对我来说似乎还可以。您可以与我找到的那个进行一个小的性能比较。作为另一种想法,您可以使用 strtok() 迭代地标记输入(而不是替换/分解)。 谢谢!实际上,我比我自己更喜欢您找到的功能,因为我发现您越深入,它的格式设置就越差。而且我从来不知道 str_repeat() 或 strtok(),所以也谢谢你!【参考方案3】:我尝试以不同的方式运行下面的代码设置formatOutput
和preserveWhiteSpace
,唯一对输出有任何影响的成员是formatOutput
。你可以运行下面的脚本,看看它是否有效?
<?php
echo "<pre>";
$foo = new DOMDocument();
//$foo->preserveWhiteSpace = false;
$foo->formatOutput = true;
$root = $foo->createElement("root");
$root->setAttribute("attr", "that");
$bar = $foo->createElement("bar", "some text in bar");
$baz = $foo->createElement("baz", "some text in baz");
$foo->appendChild($root);
$root->appendChild($bar);
$root->appendChild($baz);
echo htmlspecialchars($foo->saveXML());
echo "</pre>";
?>
【讨论】:
您的代码工作正常,但它不适用于我设置它的方式。我有一个类 xml,在该类中我创建了一个变量 $this->xml,它包含一个 DOMDocument 的实例,它似乎不适用于该设置。我也希望有真正的标签而不是空格。 这似乎是一个特例。我创建了一个以“xml”为成员的简单类,它仍然有效。有太多因素,如果没有您的确切代码(或对您来说仍然失败的简化版本),将无法重现。 感谢约翰的帮助。我已经编写了一个基本的缩进函数,希望能解决我的问题(如果你想看一下,将把它作为答案发布)。【参考方案4】:打印xml的时候调用什么方法?
我用这个:
$doc = new DOMDocument('1.0', 'utf-8');
$root = $doc->createElement('root');
$doc->appendChild($root);
(...)
$doc->formatOutput = true;
$doc->saveXML($root);
它工作得很好,但只打印出元素,所以你必须手动打印<?xml ... ?>
部分..
【讨论】:
【参考方案5】:本主题中的大多数答案都涉及 xml 文本流。 这是另一种使用 dom 功能来执行缩进工作的方法。 loadXML() dom 方法将 xml 源中存在的缩进字符导入为文本节点。想法是从 dom 中删除此类文本节点,然后重新创建格式正确的文本节点(有关详细信息,请参阅下面代码中的 cmets)。
xmlIndent() 函数作为继承自domDocument 的indentDomDocument 类的方法实现。 以下是如何使用它的完整示例:
$dom = new indentDomDocument("1.0");
$xml = file_get_contents("books.xml");
$dom->loadXML($xml);
$dom->xmlIndent();
echo $dom->saveXML();
class indentDomDocument extends domDocument
public function xmlIndent()
// Retrieve all text nodes using XPath
$x = new DOMXPath($this);
$nodeList = $x->query("//text()");
foreach($nodeList as $node)
// 1. "Trim" each text node by removing its leading and trailing spaces and newlines.
$node->nodeValue = preg_replace("/^[\s\r\n]+/", "", $node->nodeValue);
$node->nodeValue = preg_replace("/[\s\r\n]+$/", "", $node->nodeValue);
// 2. Resulting text node may have become "empty" (zero length nodeValue) after trim. If so, remove it from the dom.
if(strlen($node->nodeValue) == 0) $node->parentNode->removeChild($node);
// 3. Starting from root (documentElement), recursively indent each node.
$this->xmlIndentRecursive($this->documentElement, 0);
// end function xmlIndent
private function xmlIndentRecursive($currentNode, $depth)
$indentCurrent = true;
if(($currentNode->nodeType == XML_TEXT_NODE) && ($currentNode->parentNode->childNodes->length == 1))
// A text node being the unique child of its parent will not be indented.
// In this special case, we must tell the parent node not to indent its closing tag.
$indentCurrent = false;
if($indentCurrent && $depth > 0)
// Indenting a node consists of inserting before it a new text node
// containing a newline followed by a number of tabs corresponding
// to the node depth.
$textNode = $this->createTextNode("\n" . str_repeat("\t", $depth));
$currentNode->parentNode->insertBefore($textNode, $currentNode);
if($currentNode->childNodes)
$indentClosingTag = false;
foreach($currentNode->childNodes as $childNode) $indentClosingTag = $this->xmlIndentRecursive($childNode, $depth+1);
if($indentClosingTag)
// If children have been indented, then the closing tag
// of the current node must also be indented.
$textNode = $this->createTextNode("\n" . str_repeat("\t", $depth));
$currentNode->appendChild($textNode);
return $indentCurrent;
// end function xmlIndentRecursive
// end class indentDomDocument
【讨论】:
【参考方案6】:你偷看,
刚刚发现,一个根 XML 元素可能不包含文本子元素。这是不直观的。 F。但显然,这就是原因,例如,
$x = new \DOMDocument;
$x -> preserveWhiteSpace = false;
$x -> formatOutput = true;
$x -> loadXML('<root>a<b>c</b></root>');
echo $x -> saveXML();
将无法缩进。
https://bugs.php.net/bug.php?id=54972
所以你去,h。吨。 H。等等。
【讨论】:
【参考方案7】:header("Content-Type: text/xml");
$str = "";
$str .= "<customer>";
$str .= "<offer>";
$str .= "<opened></opened>";
$str .= "<redeemed></redeemed>";
$str .= "</offer>";
echo $str .= "</customer>";
如果您使用除.xml
以外的任何扩展名,则首先将标头Content-Type
标头设置为正确的值。
【讨论】:
以上是关于在 PHP 中使用 DOMDocument 缩进的主要内容,如果未能解决你的问题,请参考以下文章
来自DOMDocument的nodeValue在PHP中返回奇怪的字符
使用 DOMDocument PHP 获取 Xpath 父节点?
在 PHP 中从 DOMNode 创建 DOMDocument