2009-04-14 10 views
21

Ich benutze DOMDocument, um eine neue XML-Datei zu generieren, und ich möchte für die Ausgabe der Datei eingerückt werden, so dass es für einen menschlichen Leser einfach zu folgen ist.Einrückung mit DOMDocument in PHP

Wenn zum Beispiel DOMDocument gibt diese Daten:

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

ich die XML-Datei sein will:

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

Ich habe gesucht, um nach Antworten suchen, und alles, was ich Es scheint zu sagen, dass der Versuch unternommen wird, den Leerraum auf diese Weise zu steuern:

Aber das scheint nichts zu tun. Vielleicht funktioniert das nur beim Lesen von XML? Denken Sie daran, ich versuche, neue Dokumente zu schreiben.

Ist zu diesem Zweck etwas eingebaut in DOMDocument? Oder eine Funktion, die das leicht erreichen kann?

+1

Ich bin mir nicht sicher, was die Frage ist. Der Code, den Sie anzeigen, gibt die Ausgabe an, nach der Sie fragen. Beweis: http://codepad.org/4UGyRspx und http://codepad.org/bLTOFQrp - fragen Sie nach der Einrückungsebene, z. die Anzahl der verwendeten Räume? – Gordon

+0

Es gibt hier eine nette direkte Funktion (basierend auf regulären Ausdrücken): [XML mit PHP formatieren] (http://recurser.com/articles/2007/04/05/format-xml-with-php/) – Tomalak

+0

Related as solange Einrückung betroffen ist: [Einrückung mit preg_replace konvertieren (kein Rückruf)] (http://stackoverflow.com/questions/8616594/converting-indentation-with-preg-replace-no-callback) – hakre

Antwort

3

Ich habe versucht, den Code unter Einstellung formatOutput und preserveWhiteSpace auf verschiedene Arten ausgeführt, und das einzige Mitglied, das eine Auswirkung auf die Ausgabe hat, ist formatOutput. Können Sie das folgende Skript ausführen und sehen, ob es funktioniert?

+0

Ihr Code funktioniert gut, aber es funktioniert nicht für mich mit der Art, wie ich es eingerichtet habe. Ich habe eine Klasse xml und innerhalb dieser Klasse ich eine Variable $ this-> xml, die eine Instanz von DOMDocument enthält, und es scheint nicht mit dieser Einrichtung zu arbeiten. Ich würde auch lieber echte Registerkarten anstelle von nur Leerzeichen haben. –

+0

Das scheint dann ein Sonderfall zu sein. Ich habe eine einfache Klasse mit "xml" als Mitglied erstellt, und es funktionierte immer noch. Es gibt zu viele Faktoren und ohne Ihren genauen Code (oder eine vereinfachte Version, die immer noch für Sie fehlschlägt) wird es unmöglich sein, sie zu reproduzieren. –

+0

Danke für Ihre Hilfe John. Ich habe eine grundlegende Einrückungsfunktion geschrieben, die hoffentlich mein Problem behebt (um es als eine Antwort zu veröffentlichen, wenn Sie einen Blick darauf werfen wollen). –

7

Nach einiger Hilfe von John und spielen mit diesem auf eigene Faust, scheint es, dass sogar DOMDocument inhärente Unterstützung für die Formatierung nicht meine Bedürfnisse erfüllt. Also entschied ich mich, meine eigene Einrückungsfunktion zu schreiben.

Das ist eine ziemlich grobe Funktion, die ich einfach schnell zusammengeworfen habe. Wenn also jemand Optimierungstipps hat oder irgendetwas zu sagen, würde ich mich freuen, das zu hören!

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

Eine kurze Bemerkung: Es gibt str_repeat() für das Erstellen der Registerkarten. Der Rest der Funktion scheint mir ganz in Ordnung zu sein. Sie könnten einen kleinen Leistungsvergleich zu dem erstellen, den ich gefunden habe. Als eine alternative Idee können Sie strtok() verwenden, um die Eingabe iterativ zu tokenisieren (anstelle von Ersetzen/Explodieren). – Tomalak

+0

Danke! Ich mag die Funktion, die du besser findest als meine eigene, da ich festgestellt habe, dass die Formatierung schlechter ist, je tiefer du gehst. Und ich wusste nie über str_repeat() oder strtok(), also danke auch dafür! –

-2
header("Content-Type: text/xml"); 

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

Wenn Sie den Header Content-Type Header auf den richtigen Wert verwenden gesetzt jede andere Erweiterung als .xml dann zuerst.

1

Welche Methode rufen Sie beim Drucken der XML auf?

Ich benutze diese:

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

(...)

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

Es funktioniert perfekt, aber nur das Element druckt, so dass Sie den <?xml ... ?> Teil manuell drucken müssen ..

24

DomDocument den Trick, ich paar Stunden persönlich verbrachte googeln und versucht, dies herauszufinden, und ich stellte fest, dass, wenn Sie

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

In dieser Reihenfolge verwenden, es funktioniert einfach nicht aber, wenn Sie verwenden den gleichen Code aber in dieser Reihenfolge:

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

wirkt wie ein Zauber, hoffen, dass diese

+2

Alter! Du schaukelst! Danke, dass du es gesehen hast! –

+3

Verdammt ... Das scheint nur mit XML zu funktionieren, HTML sieht immer noch hässlich aus. =/ –

+0

Ja, scheint nicht für HTML zu arbeiten – 3zzy

1

meisten Antworten in diesem Thema befassen sich mit xML-Textfluss hilft. Hier ist ein weiterer Ansatz, bei dem die Dom-Funktionen zum Ausführen des Einrückungsjobs verwendet werden. Die loadXML() -Dom-Methode importiert Einrückungszeichen, die in der XML-Quelle als Textknoten vorhanden sind. Die Idee besteht darin, solche Textknoten aus dem dom zu entfernen und dann korrekt formatierte zu erstellen (siehe Kommentare im folgenden Code für weitere Details).

Die Funktion xmlIndent() ist als eine Methode der Klasse indentDomDocument implementiert, die von domDocument geerbt wird. Unten finden Sie ein komplettes Beispiel dafür, wie es zu benutzen:

$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

Yo blickte,

nur, dass offenbar herausgefunden, ein XML-Stammelement nicht Text Kinder enthält. Dies ist nicht intuitiv a. f. Aber anscheinend ist dies der Grund dafür, dass zum Beispiel

nicht einrücken können.

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

Also los gehts, h. t. h. etc.