Отступ с DOMDocument в PHP - PullRequest
       24

Отступ с DOMDocument в PHP

23 голосов
/ 14 апреля 2009

Я использую 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 для этого? Или функция, которая может выполнить это легко?

Ответы [ 7 ]

28 голосов
/ 25 января 2013

DomDocument справится, я лично потратил пару часов на поиски в Google и пытался понять это, и я заметил, что если вы используете

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

Работает как шарм, надеюсь, это поможет

7 голосов
/ 14 апреля 2009

После некоторой помощи от Джона и самостоятельной игры с этим кажется, что даже внутренняя поддержка форматирования в 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;
}
3 голосов
/ 14 апреля 2009

Я попытался запустить приведенный ниже код с настройками formatOutput и preserveWhiteSpace по-разному, и единственный член, который как-то влияет на вывод, - formatOutput. Можете ли вы запустить скрипт ниже и посмотреть, работает ли он?

<code><?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 "
"; ?>
1 голос
/ 16 августа 2013

Большинство ответов в этой теме касаются потока текста 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
1 голос
/ 20 апреля 2012

Какой метод вы вызываете при печати xml?

Я использую это:

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

(...)

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

Работает отлично, но печатает только элемент, поэтому вы должны напечатать <?xml ... ?> часть вручную ..

0 голосов
/ 23 июля 2017

Йо выглядывает,

только что обнаружил, что, очевидно, корневой элемент XML может не содержать текстовых дочерних элементов Это не интуитивно понятно. е. Но, видимо, это причина того, что, например,

$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

Итак, поехали, ч. т. час и т. д.

0 голосов
/ 23 августа 2009
header("Content-Type: text/xml");

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

Если вы используете любое расширение, кроме .xml, то сначала установите правильное значение заголовка Content-Type.

...