2010-09-06 5 views
1

Im travaillant avec PHP5, et je dois transformer XML sous la forme suivante:Transformer la structure XML à une autre structure xml

<item> 
    <string isNewLine="1" lineNumber="32">some text in new line</string> 
    <string>, more text</string> 
    <item> 
     <string isNewLine="1" lineNumber="33">some text in new line</string> 
     <string isNewLine="1" lineNumber="34">some text</string> 
     <string> in the same line</string> 
     <string isNewLine="1" lineNumber="35">some text in new line</string> 
    </item> 
</item> 

en quelque chose comme ceci:

<item> 
    <line lineNumber="32">some text in new line, more text</string> 
    <item> 
      <line lineNumber="33">some text in new line</string> 
      <line lineNumber="34">some text in the same line</string> 
      <line lineNumber="35">some text in new line</string> 
    </item> 
</item> 

Comme vous pouvez le voir, il a rejoint le texte contenu dans plusieurs nœuds 'chaîne'. Notez également que les nœuds «chaîne» peuvent être imbriqués dans d'autres nœuds à n'importe quel niveau.

Quelles sont les solutions possibles pour transformer le XML source en XML cible?

Merci,

+0

Vérifiez les commentaires à la réponse acceptée, le code XSLT il y a toujours un bug. – Tomalak

+0

Bonne question (+1). Voir ma réponse pour la seule solution correcte. La solution que vous avez acceptée n'est pas du tout correcte - exécutez-la simplement et comparez les résultats avec ce que vous voulez vraiment. –

+0

'@ isNewLine' semble être redondant avec' @ lineNumber': si un 'string' a un' @ lineNumber', il a toujours '@ isNewLine' et l'inverse est vrai. – dolmen

Répondre

2

Voici une solution efficace et correcte:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:key name="knextStrings" 
    match="string[not(@isNewLine)]" 
    use="generate-id(preceding-sibling::string 
           [@isNewLine][1] 
        )"/> 


<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="string[@isNewLine]"> 
    <line> 
    <xsl:copy-of select="@*[not(name()='isNewLine')]"/> 
    <xsl:copy-of select="text() 
         | 
         key('knextStrings', 
          generate-id() 
          ) 
           /text()"/> 
    </line> 
</xsl:template> 

<xsl:template match="string[not(@isNewLine)]"/> 
</xsl:stylesheet> 

lorsque cette transformation est appliquée sur le document XML fourni initialement:

<item> 
    <string isNewLine="1" lineNumber="32">some text in new line</string> 
    <string>, more text</string> 
    <item> 
     <string isNewLine="1" lineNumber="33">some text in new line</string> 
     <string isNewLine="1" lineNumber="34">some text</string> 
     <string> in the same line</string> 
     <string isNewLine="1" lineNumber="35">some text in new line</string> 
    </item> 
</item> 

le résultat recherché, est produit correct:

<item> 
    <line lineNumber="32">some text in new line, more text</line> 
    <item> 
    <line lineNumber="33">some text in new line</line> 
    <line lineNumber="34">some text in the same line</line> 
    <line lineNumber="35">some text in new line</line> 
    </item> 
</item> 
+0

+1 pour une sortie correcte et parce que c'est ce que je pensais;) –

+0

+1 Pour une solution efficace et correcte. –

+0

Cette solution fonctionne de la boîte. Ce serait génial si vous pouviez ajouter des commentaires sur le code. –

2

Cette feuille de style produit la sortie que vous recherchez:

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

    <!--Identity template simply copies content forward by default --> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="string[@isNewLine and @lineNumber]"> 
     <line> 
      <xsl:apply-templates select="@*"/> 
      <xsl:apply-templates select="text()" /> 
      <!-- Include the text() from the string elements that come after this element, 
       do not have @isNewLine or @lineNumber, 
       and are only following this particular element --> 
      <xsl:apply-templates select="following-sibling::string[not(@isNewLine and @lineNumber) and generate-id(preceding-sibling::string[1]) = generate-id(current())]/text()" /> 
     </line> 
    </xsl:template> 

    <!--Suppress the string elements that do not contain isNewLine or lineNumber attributes in normal processing--> 
    <xsl:template match="string[not(@isNewLine and @lineNumber)]" /> 

    <!--Empty template to prevent attribute from being copied to output--> 
    <xsl:template match="@isNewLine" /> 

</xsl:stylesheet> 
+0

+1 Vous devez copier tout sauf l'attribut 'isNewLine'. – Tomalak

+1

@Mads Hansen: +1 Pour le motif correct. Édition mineure: transformation 'chaîne' en ligne, dépouille' @ isNewLine' et réduit le prédicat. –

+0

Merci @Tomalak, bonne prise. @Alejandro, j'ai ajouté un template vide pour @isNewLine. Se sent plus propre à moi d'avoir le modèle vide plutôt que d'exclure dans le filtre de prédicat (6 sur un, une demi douzaine d'un autre). –

0

Utilisez une transformation XSL.

De l'PHP documentation:

<?php 

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

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

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

echo $proc->transformToXML($xml); 

?> 

Utilisez la réponse de Dimitri pour trans.xsl.

Questions connexes