parser example php xml whitespace indentation domdocument

example - save xml php



SangrĂ­a con DOMDocument en PHP (7)

Estoy usando DOMDocument para generar un nuevo archivo XML y me gustaría que la salida del archivo esté bien sangrada para que sea fácil de seguir para un lector humano.

Por ejemplo, cuando DOMDocument produce estos datos:

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

Quiero que el archivo XML sea:

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

He estado buscando respuestas y todo lo que he encontrado parece decir que hay que intentar controlar el espacio en blanco de esta manera:

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

Pero esto no parece hacer nada. Tal vez esto solo funciona cuando se lee XML? Ten en cuenta que estoy intentando escribir nuevos documentos.

¿Hay algo integrado en DOMDocument para hacer esto? ¿O una función que puede lograr esto fácilmente?


¿A qué método llamas al imprimir el xml?

Yo uso esto:

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

(...)

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

Funciona perfectamente pero imprime solo el elemento, por lo que debe imprimir la parte <?xml ... ?> Manualmente ...


Ase

Acabo de descubrir que, aparentemente, un elemento XML raíz no puede contener elementos secundarios de texto. Esto no es intuitivo pero, aparentemente, esta es la razón por la que, por ejemplo,

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

dejará de sangrar

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

Así que ahí tienes, hth et c.


Después de recibir ayuda de John y jugar con esto por mi cuenta, parece que incluso el soporte inherente de DOMDocument para el formateo no satisfacía mis necesidades. Entonces, decidí escribir mi propia función de sangría.

Esta es una función bastante burda que acabo de juntar rápidamente, así que si alguien tiene algún consejo de optimización o algo que decir sobre esto en general, ¡me encantaría escucharlo!

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


DomDocument hará el truco, yo personalmente pasé un par de horas buscando en Google y tratando de resolver esto y noté que si usas

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

En ese orden, simplemente no funciona pero, si usa el mismo código pero en este orden:

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

Funciona como un encanto, espero que esto ayude.


He intentado ejecutar el siguiente código configurando formatOutput y preserveWhiteSpace de diferentes maneras, y el único miembro que tiene algún efecto en la salida es formatOutput . ¿Puedes ejecutar el script de abajo y ver si funciona?

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


La mayoría de las respuestas en este tema tratan con el flujo de texto xml. Aquí hay otro enfoque que utiliza las funciones de dom para realizar el trabajo de sangría. El método dom loadXML () importa los caracteres de sangría presentes en la fuente xml como nodos de texto. La idea es eliminar dichos nodos de texto del dominio y luego volver a crear los que tienen el formato correcto (consulte los comentarios en el código a continuación para obtener más detalles).

La función xmlIndent () se implementa como un método de la clase indentDomDocument, que se hereda de domDocument. A continuación se muestra un ejemplo completo de cómo usarlo:

$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


header("Content-Type: text/xml"); $str = ""; $str .= "<customer>"; $str .= "<offer>"; $str .= "<opened></opened>"; $str .= "<redeemed></redeemed>"; $str .= "</offer>"; echo $str .= "</customer>";

Si está utilizando una extensión que no sea .xml , primero configure el encabezado Content-Type header en el valor correcto.