Подтвердить что ты не робот

Отступ с DOMDocument в PHP

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

<?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>";
?>

Ответ 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, а затем воссоздать правильно отформатированные (см. Комментарии в приведенном ниже коде для более подробной информации).

Функция 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

Ответ 6

Yo peeps,

только что выяснил, что, судя по всему, корневой элемент 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

Итак, вы идете, h. т. час et c.

Ответ 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 на правильное значение.