2017-01-27 2 views
0

Я ищу, чтобы попытаться изменить вывод XML таким образом, что структура элемента изменяется и некоторые CDATA становится attribute, а не <element>Изменение элемента для атрибута PHP/DOM (XML)

Учитывая XML stack.xml:

<root> 
    <item> 
    <name>name</name> 
    <type>Type</type> 
    <dateMade>Datemade</dateMade> 
    <desc>Desc</desc> 
    </item> 
....(more Items)... 
</root> 

Я хотел бы изменить вывод XML в stacksaved.xml:

<root> 
    <item> 
    <name>name</name> 
    <Itemtype type="Type"> 
     <Itemdate dateMade="Datemade"> 
      <desc>Desc</desc> 
     </Itemdate> 
    <Itemtype> 
    </item> 
....(next item).... 
</root> 

до сих пор мой PHP DOM выглядит л икэ это:

<?php 
    //create and load 
    $doc = new DOMDocument(); 
    $doc->load('stack.xml'); 

    $types=$doc->getElementsByTagName("type"); 
    foreach ($types as $type) 
    { 
     $attribute=$doc->getElementsByTagName("type"); 
     $doc->getElementsByTagName("type").setAttribute("$attribute"); 
    } 
    $doc->save('stacksaved.xml'); //save the final results into xml file 
?> 

я получаю ошибку: Фатальная ошибка: Вызов неопределенной функции SetAttribute(), и документ не будет сохранен или отредактирован в любом случае. Я действительно новичок в DOM/PHP и очень ценю любые советы!

Как будет идти об изменении структуры ребенка и элемента к нужному выходу?

Спасибо как всегда за прочитанное!

EDIT: Parfait дал большое объяснение и показал большую силу XSLT, но я пытаюсь заставить его работать с использованием чистого php только как учебное упражнение для php/DOM. Может ли кто-нибудь помочь с преобразованием этого только с помощью PHP?

+0

Не изменяйте оригинал, создайте новый целевой документ с информацией из исходного документа. Используйте 'DOMXpath :: evaluation()' для извлечения данных из исходного документа. – ThW

ответ

1

Для pure Решение PHP DOM, рассмотрите возможность создания нового DOMDocument, итерации по значениям старого документа с использованием методов createElement, appendChild и setAttribute. Многократная вложенная логика if необходима для проверки существования узла перед созданием элементов с значениями узлов элементов, иначе Неопределенные предупреждения подняты.

$doc = new DOMDocument(); 
$doc->load('stack.xml'); 

// INITIALIZE NEW DOM DOCUMENT 
$newdoc = new DOMDocument('1.0', 'UTF-8'); 
$newdoc->preserveWhiteSpace = false; 
$newdoc->formatOutput = true; 

// APPEND ROOT 
$root= $newdoc->appendChild($newdoc->createElement("root")); 

$items=$doc->getElementsByTagName("item"); 

// ITERATIVELY APPEND ITEM AND CHILDREN 
foreach($items as $item){  
    $ItemNode = $newdoc->createElement("item"); 
    $root->appendChild($ItemNode); 

    if (count($item->getElementsByTagName("name")->item(0)) > 0) { 
     $ItemNode->appendChild($newdoc->createElement('name', $item->getElementsByTagName("name")->item(0)->nodeValue)); 
    } 

    if (count($item->getElementsByTagName("type")->item(0)) > 0) {   
     $ItemtypeNode = $ItemNode->appendChild($newdoc->createElement('Itemtype')); 
     $ItemtypeNode->setAttribute("type", $item->getElementsByTagName("type")->item(0)->nodeValue); 

     if (count($item->getElementsByTagName("dateMade")->item(0)) > 0) { 
      $ItemdateNode = $ItemtypeNode->appendChild($newdoc->createElement('Itemdate')); 
      $ItemdateNode->setAttribute("dateMade", $item->getElementsByTagName("dateMade")->item(0)->nodeValue); 

      if (count($item->getElementsByTagName("desc")->item(0)) > 0) { 
       $ItemdateNode->appendChild($newdoc->createElement('desc', $item->getElementsByTagName("desc")->item(0)->nodeValue)); 
      } 
     } 

    } 
} 

// ECHO AND SAVE NEW DOC TREE 
echo $newdoc->saveXML(); 
$newdoc->save($cd.'/ItemTypeDateMade_dom.xml'); 

Выход

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <item> 
    <name>name</name> 
    <Itemtype type="Type"> 
     <Itemdate dateMade="Datemade"> 
     <desc>Desc</desc> 
     </Itemdate> 
    </Itemtype> 
    </item> 
</root> 

Как уже упоминалось в предыдущем ответе, здесь требуется for и вложенную if, которые не требуются при XSLT. Фактически, используя microtime, мы можем сравнить время выполнения скриптов. Ниже укрупнить stack.xml:

$time_start = microtime(true); 
... 
echo "Total execution time in seconds: " . (microtime(true) - $time_start) ."\n"; 

На 1000 узлов линий, XSLT оказывается быстрее, чем DOM:

# XSLT VERSION 
Total execution time in seconds: 0.0062189102172852 

# DOM VERSION 
Total execution time in seconds: 0.013695955276489 

At 2000 узловые линии, XSLT по-прежнему остается около 2 раза быстрее, чем DOM:

# XSLT VERSION 
Total execution time in seconds: 0.014697074890137 

# DOM VERSION 
Total execution time in seconds: 0.031282186508179 

На 10000 узловых линий XSLT теперь становится немного быстрее, чем DOM. Причина для догонов DOM может быть связана с неэффективностью памяти XSLT 1.0 для больших файлов, особенно (> 100 МБ). Но, возможно, здесь для этого варианта использования XSLT-подход является более легким сценарием PHP для поддержания и чтения:

# XSLT VERSION 
Total execution time in seconds: 0.27568817138672 

# DOM VERSION 
Total execution time in seconds: 0.37149095535278 
+0

благодарим за дополнительную помощь и информацию. Удивительно видеть разницу в скорости большого процесса и видеть, как XSLT является гораздо более эффективным вариантом. Еще раз спасибо! Хорошего дня. :) – Ben

1

Рассмотрите XSLT, специальный, декларативный язык, предназначенный для преобразования документов XML. PHP может запускать скрипты XSLT 1.0 с расширением php-xsl (обязательно включите его в .ini-файле). При таком подходе вы избегаете использования цикла foreach или if.

XSLT(сохранить как .xsl файл)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
<xsl:output version="1.0" encoding="UTF-8" indent="yes" /> 

    <xsl:template match="root"> 
    <xsl:copy> 
    <xsl:apply-templates select="item"/> 
    </xsl:copy> 
    </xsl:template>  

    <xsl:template match="item"> 
    <xsl:copy> 
    <xsl:copy-of select="name"/> 
    <Itemtype type="{type}"> 
     <Itemdate dateMade="{dateMade}"> 
     <xsl:copy-of select="desc"/> 
     </Itemdate> 
    </Itemtype>  
    </xsl:copy> 
    </xsl:template> 

</xsl:transform> 

PHP

$doc = new DOMDocument(); 
$doc->load('stack.xml'); 

$xsl = new DOMDocument; 
$xsl->load('XSLTScript.xsl'); 

// CONFIGURE TRANSFORMER 
$proc = new XSLTProcessor; 
$proc->importStyleSheet($xsl); 

// PROCESS TRANSFORMATION 
$newXML = $proc->transformToXML($doc); 

// ECHO STRING OUTPUT 
echo $newXML; 

// SAVE OUTPUT TO FILE 
file_put_contents('Output.xml', $newXML); 

Выход

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <item> 
    <name>name</name> 
    <Itemtype type="Type"> 
     <Itemdate dateMade="Datemade"> 
     <desc>Desc</desc> 
     </Itemdate> 
    </Itemtype> 
    </item> 
</root> 
+0

Спасибо за быстрый ответ @parfait! XSLT - отличный инструмент преобразования, но я пытаюсь научиться делать это с помощью чистого PHP без внешнего XSL-файла. Это возможно? – Ben

+1

Это все равно будет работать. Вы можете хранить XSLT в виде строки внутри скрипта и использовать 'DOMDocument :: loadXml()' для его загрузки. – ThW

+1

Действительно, XSLT является хорошо сформированным XML-файлом и может анализироваться из строки или файла. – Parfait