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