2009-04-14 8 views
21

Я использую DOMDocument для создания нового XML-файла, и я хотел бы, чтобы выход файла был отступом хорошо, так что его можно легко отслеживать для читателя.Отступ с DOMDocument в PHP

Например, когда 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

Я не уверен, в чем вопрос. Код, который вы показываете, даст результат, о котором вы просите. Доказательство: http://codepad.org/4UGyRspx и http://codepad.org/bLTOFQrp - вы спрашиваете о уровне отступов, например. количество используемых пробелов? – Gordon

+0

Здесь есть простая простая функция (на основе регулярных выражений): [Формат XML с PHP] (http://recurser.com/articles/2007/04/05/format-xml-with-php/) – Tomalak

+0

долгое время как отступ: [Преобразование отступов с preg_replace (без обратного вызова)] (http://stackoverflow.com/questions/8616594/converting-indentation-with-preg-replace-no-callback) – hakre

ответ

3

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

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

Ваш код работает нормально, но это не работает для меня с тем, как я его настроил. У меня есть класс xml и внутри этого класса я создаю переменную $ this-> xml, которая содержит экземпляр DOMDocument и, похоже, не работает с этой установкой. Я бы предпочел иметь реальные вкладки вместо просто пробелов. –

+0

Это похоже на частный случай. Я создал простой класс с «xml» в качестве участника, и он все еще работал. Слишком много факторов, и без вашего точного кода (или упрощенной версии, которая все еще терпит неудачу для вас) это будет невозможно воспроизвести. –

+0

Спасибо за помощь. Я написал основную функцию отступов, которая, надеюсь, исправит мою проблему (собираюсь опубликовать ее в качестве ответа, если вы хотите взглянуть). –

7

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

Быстрое замечание: для создания вкладок существует str_repeat(). Остальная функция кажется мне вполне подходящей. Вы можете настроить небольшое сравнение производительности с тем, которое я нашел. В качестве альтернативной идеи вы можете использовать strtok() для токенизации ввода итеративно (вместо замены/взорвать). – Tomalak

+0

Спасибо! Мне действительно нравится функция, которую вы нашли лучше, чем моя, поскольку я обнаружил, что форматирование сильно глубже вы идете. И я никогда не знал ни о str_repeat(), ни о strtok(), поэтому спасибо за это! –

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

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

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

1

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

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

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

(...)

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

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

24

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

$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

Чувак! Ты жжешь! Спасибо, что заметили это! –

+3

Dammit ... Это только кажется, что работает с XML, HTML по-прежнему выглядит уродливым. =/ –

+0

Да, похоже, не работает для HTML – 3zzy

1

Большинство ответов в этой теме сделки с 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

Yo выглядывает,

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

не сможет отступать.

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

Так что вы идете, ч. т. час и т.п.