27

我正在使用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可以做到这一点吗?还是可以轻松完成此任务的功能?

4

7 回答 7

34

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

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

于 2013-01-25T06:15:46.987 回答
7

在约翰的一些帮助和我自己玩弄这个之后,似乎即使是 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;
}
于 2009-04-14T05:27:53.010 回答
3

我尝试过以不同方式运行设置下面的代码formatOutputpreserveWhiteSpace唯一对输出有任何影响的成员是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>";
?>
于 2009-04-14T03:38:57.980 回答
1

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

xmlIndent() 函数实现为 indentDomDocument 类的方法,该类继承自 domDocument。以下是如何使用它的完整示例:

$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
于 2013-08-15T20:12:26.160 回答
1

打印 xml 时调用哪种方法?

我用这个:

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

(...)

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

它工作得很好,但只打印出元素,所以你必须<?xml ... ?>手动打印部分。

于 2012-04-20T10:01:32.567 回答
-1

哟偷看,

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

$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

所以你去,hth等。

于 2017-07-23T01:05:24.337 回答
-3
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 .xml then first set the header Content-Type header to the correct value.

于 2009-08-23T10:37:52.360 回答