在 PHP 中使用 DOMDocument 进行缩进

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/746238/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 23:44:18  来源:igfitidea点击:

Indentation with DOMDocument in PHP

phpxmlwhitespaceindentationdomdocument

提问by Josh Leitzel

I'm using DOMDocumentto generate a new XML file and I would like for the output of the file to be indented nicely so that it's easy to follow for a human reader.

我正在使用DOMDocument生成一个新的 XML 文件,我希望该文件的输出能够很好地缩进,以便人类读者可以轻松理解。

For example, when DOMDocumentoutputs this data:

例如,当DOMDocument输出此数据时:

<?xml version="1.0"?>
<this attr="that"><foo>lkjalksjdlakjdlkasd</foo><foo>lkjlkasjlkajklajslk</foo></this>

I want the XML file to be:

我希望 XML 文件是:

<?xml version="1.0"?>
<this attr="that">
    <foo>lkjalksjdlakjdlkasd</foo>
    <foo>lkjlkasjlkajklajslk</foo>
</this>

I've been searching around looking for answers, and everything that I've found seems to say to try to control the white space this way:

我一直在四处寻找答案,我发现的所有内容似乎都试图以这种方式控制空白:

$foo = new DOMDocument();
$foo->preserveWhiteSpace = false;
$foo->formatOutput = true;

But this does not seem to do anything. Perhaps this only works when reading XML? Keep in mind I'm trying to write new documents.

但这似乎没有任何作用。也许这仅在读取 XML 时有效?请记住,我正在尝试编写新文档。

Is there anything built-in to DOMDocumentto do this? Or a function that can accomplish this easily?

有没有内置的东西DOMDocument可以做到这一点?或者可以轻松完成此操作的功能?

回答by Angel

DomDocument will do the trick, I personally spent couple of hours Googling and trying to figure this out and I noted that if you use

DomDocument 可以解决这个问题,我个人花了几个小时在谷歌上搜索并试图解决这个问题,我注意到如果你使用

$xmlDoc = new DOMDocument ();
$xmlDoc->loadXML ( $xml );
$xmlDoc->preserveWhiteSpace = false;
$xmlDoc->formatOutput = true;
$xmlDoc->save($xml_file);

In that order, It just doesn't work but, if you use the same code but in this order:

按照这个顺序,它不起作用,但是,如果您使用相同的代码但按以下顺序:

$xmlDoc = new DOMDocument ();
$xmlDoc->preserveWhiteSpace = false;
$xmlDoc->formatOutput = true;
$xmlDoc->loadXML ( $xml );
$xmlDoc->save($archivoxml);

Works like a charm, hope this helps

像魅力一样工作,希望这会有所帮助

回答by Josh Leitzel

After some help from John and playing around with this on my own, it seems that even DOMDocument's inherent support for formatting didn't meet my needs. So, I decided to write my own indentation function.

在 John 的一些帮助和我自己的尝试之后,似乎即使 DOMDocument 对格式的固有支持也不能满足我的需求。所以,我决定编写自己的缩进函数。

This is a pretty crude function that I just threw together quickly, so if anyone has any optimization tips or anything to say about it in general, I'd be glad to hear it!

这是一个非常粗糙的功能,我只是快速拼凑起来,所以如果有人有任何优化技巧或任何关于它的一般说法,我会很高兴听到它!

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;
}

回答by John Rasch

I have tried running the code below setting formatOutputand preserveWhiteSpacein different ways, and the only member that has any effect on the output is formatOutput. Can you run the script below and see if it works?

我曾尝试以下运行设定的代码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>";
?>

回答by frob59

Most answers in this topic deal with xml text flow. Here is another approach using the dom functionalities to perform the indentation job. The loadXML() dom method imports indentation characters present in the xml source as text nodes. The idea is to remove such text nodes from the dom and then recreate correctly formatted ones (see comments in the code below for more details).

本主题中的大多数答案都涉及 xml 文本流。这是使用 dom 功能执行缩进作业的另一种方法。loadXML() dom 方法将 xml 源中存在的缩进字符导入为文本节点。这个想法是从 dom 中删除这样的文本节点,然后重新创建正确格式的节点(有关更多详细信息,请参阅下面代码中的注释)。

The xmlIndent() function is implemented as a method of the indentDomDocument class, which is inherited from domDocument. Below is a complete example of how to use it :

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

回答by Jindra

Which method do you call when printing the xml?

打印xml时调用哪个方法?

I use this:

我用这个:

$doc = new DOMDocument('1.0', 'utf-8');
$root = $doc->createElement('root');
$doc->appendChild($root);

(...)

(……)

$doc->formatOutput = true;
$doc->saveXML($root);

It works perfectly but prints out only the element, so you must print the <?xml ... ?>part manually..

它运行良好,但仅打印出元素,因此您必须<?xml ... ?>手动打印该部分。

回答by blaaaaaaaaaaa

Yo peeps,

哟偷看,

just found out that apparently, a root XML element may not contain text children. This is nonintuitive a. f. But apparently, this is the reason that, for instance,

刚刚发现显然,根 XML 元素可能不包含文本子元素。这是不直观的 af 但显然,这就是原因,例如,

$x = new \DOMDocument;
$x -> preserveWhiteSpace = false;
$x -> formatOutput = true;
$x -> loadXML('<root>a<b>c</b></root>');
echo $x -> saveXML();

will fail to indent.

将无法缩进。

https://bugs.php.net/bug.php?id=54972

https://bugs.php.net/bug.php?id=54972

So there you go, h. t. h. et c.

所以你去了,等等。

回答by blaaaaaaaaaaa

header("Content-Type: text/xml");

$str = "";
$str .= "<customer>";
$str .= "<offer>";
$str .= "<opened></opened>";
$str .= "<redeemed></redeemed>";
$str .= "</offer>";
echo $str .= "</customer>";

If you are using any extension other than .xmlthen first set the header Content-Typeheader to the correct value.

如果您使用除此之外的任何扩展名,请.xml首先将标Content-Type头标头设置为正确的值。