2009-05-20 10 views
3

j'ai une structure XML suivante que je dois transformer:Transformer XML en XML avec XSLT - export ColdFusion laid

<recordset rowCount="68" fieldNames="ITEM,ECL,LEAD_TIME" type="**coldfusion.sql.QueryTable**"> 
<field name="ITEM"> 
<string>ITEM_A</string> 
<string>ITEM_B</string> 
<string>ITEM_C</string> 
</field> 
<field name="REV"> 
<string>A</string> 
<string>B</string> 
<string>C</string> 
</field> 
<field name="LEAD_TIME"> 
<string>10</string> 
<string>15</string> 
<string>25</string> 
</field> 
</recordset> 

en:

<records> 
<item_line> 
<item>ITEM_A</item> 
<rev>A</rev> 
<lead_time>10</lead_time> 
</item_line> 
<item_line> 
<item>ITEM_B</item> 
<rev>B</rev> 
<lead_time>15</lead_time> 
</item_line> 
<item_line> 
<item>ITEM_C</item> 
<rev>C</rev> 
<lead_time>25</lead_time> 
</item_line> 
</records> 

Ma connaissance de XSLT est très limitée ..

Merci d'avance!

Répondre

4
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /> 

    <xsl:template match="/recordset"> 
    <records> 
     <xsl:apply-templates select="field[@name='ITEM']/string" /> 
    </records> 
    </xsl:template> 

    <xsl:template match="field[@name='ITEM']/string"> 
    <xsl:variable name="currpos" select="position()" /> 

    <item_line> 
     <item> 
     <xsl:value-of select="." /> 
     </item> 
     <rev> 
     <xsl:value-of select="/recordset/field[@name='REV']/string[$currpos]" /> 
     </rev> 
     <lead_time> 
     <xsl:value-of select="/recordset/field[@name='LEAD_TIME']/string[$currpos]" /> 
     </lead_time> 
    </item_line> 
    </xsl:template> 

</xsl:stylesheet> 

Un peu plus lisible et probablement légèrement version plus rapide serait d'utiliser un <xsl:key>:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /> 

    <xsl:key name="k_field" match="recordset/field" use="@name" /> 

    <xsl:template match="/"> 
    <records> 
     <xsl:apply-templates select="key('k_field', 'ITEM')/string" /> 
    </records> 
    </xsl:template> 

    <xsl:template match="field[@name='ITEM']/string"> 
    <xsl:variable name="currpos" select="position()" /> 

    <item_line> 
     <item> 
     <xsl:value-of select="." /> 
     </item> 
     <rev> 
     <xsl:value-of select="key('k_field', 'REV')/string[$currpos]" /> 
     </rev> 
     <lead_time> 
     <xsl:value-of select="key('k_field', 'LEAD_TIME')/string[$currpos]" /> 
     </lead_time> 
    </item_line> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Je pense que vous oublié la chaîne/après l'état du régime et lead_time- – Scoregraphic

+0

Ouais ... sélectionne fixe. Merci de m'avoir signalé. :-) – Tomalak

+0

Merci! Je vais tester cela dès que possible. –

2

Une approche simple:

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

    <xsl:template match="//recordset"> 
     <records> 
      <xsl:apply-templates select="field[@name = 'ITEM']/string"/> 
     </records> 
    </xsl:template> 

    <xsl:template match="string"> 
     <xsl:variable name="loc" select="position()"/> 
     <item_line> 
      <item> 
       <xsl:value-of select="."/> 
      </item> 
      <rev> 
       <xsl:value-of select="//recordset/field[@name = 'REV']/string[position() = $loc]"/> 
      </rev> 
      <lead_time> 
       <xsl:value-of select="//recordset/field[@name = 'LEAD_TIME']/string[position() = $loc]"/> 
      </lead_time> 
     </item_line> 
    </xsl:template> 

</xsl:stylesheet> 

Et ci-dessous est un xslt plus compliqué que ci-dessus. Cela vous permettra d'étendre encore plus revTemplate et leadTimeTemplate sans encombrer le modèle de chaîne.

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

    <xsl:template match="//recordset"> 
     <records> 
      <xsl:apply-templates select="field[@name = 'ITEM']/string"/> 
     </records> 
    </xsl:template> 

    <xsl:template match="string">  
     <item_line> 
      <item> 
       <xsl:value-of select="."/> 
      </item> 
      <xsl:call-template name="revTemplate"> 
       <xsl:with-param name="loc" select="position()"/> 
      </xsl:call-template> 
      <xsl:call-template name="leadTimeTemplate"> 
       <xsl:with-param name="loc" select="position()"/> 
      </xsl:call-template> 
     </item_line> 
    </xsl:template> 

    <xsl:template name="revTemplate"> 
     <xsl:param name="loc"/> 
     <rev> 
      <xsl:value-of select="//recordset/field[@name = 'REV']/string[position() = $loc]"/> 
     </rev> 
    </xsl:template> 

    <xsl:template name="leadTimeTemplate"> 
     <xsl:param name="loc"/> 
     <rev> 
      <xsl:value-of select="//recordset/field[@name = 'LEAD_TIME']/string[position() = $loc]"/> 
     </rev> 
    </xsl:template> 

</xsl:stylesheet> 
2

La solution suivante fonctionne pour n'importe quel nom de champ et n'importe quel nombre de champs. Le format de vos données suggère que les noms de champs peuvent être dynamiques. Il ne suppose pas non plus que les enfants du champ seront toujours nommés "string". (Le nom « string » m'a fait suspecte que d'autres types de données peuvent apparaître. Qu'ils le fassent ou non, cette solution fonctionne toujours.)

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output indent="yes"/> 

    <xsl:template match="/"> 
    <records> 
     <!-- Just process the first field's children, 
      to get the list of line items --> 
     <xsl:apply-templates select="/recordset/field[1]/*"/> 
    </records> 
    </xsl:template> 

    <!-- Convert each field child to a line item --> 
    <xsl:template match="field/*"> 
    <item_line> 
     <!-- Then query all the fields for the value at this position --> 
     <xsl:apply-templates select="/recordset/field"> 
     <xsl:with-param name="pos" select="position()"/> 
     </xsl:apply-templates> 
    </item_line> 
    </xsl:template> 

    <xsl:template match="field"> 
    <xsl:param name="pos"/> 
    <!-- Convert the field name to lower case --> 
    <xsl:element name="{translate(
     @name, 
     'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
     'abcdefghijklmnopqrstuvwxyz' 
    )}"> 
     <xsl:value-of select="*[$pos]"/> 
    </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

Laissez-moi savoir si vous avez des questions.


EDIT: Une version simplifiée de ce qui précède:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output indent="yes"/> 

    <xsl:template match="/"> 
    <records> 
     <xsl:for-each select="/recordset/field[1]/*"> 
     <xsl:variable name="pos" select="position()" /> 
     <item_line> 
      <xsl:apply-templates select="/recordset/field/*[$pos]" /> 
     </item_line> 
     </xsl:for-each> 
    </records> 
    </xsl:template> 

    <xsl:template match="field/*"> 
    <xsl:element name="{translate(
     ../@name, 
     'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
     'abcdefghijklmnopqrstuvwxyz' 
    )}"> 
     <xsl:value-of select="."/> 
    </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 
+0

+1 - Très belle approche générique! J'ai ajouté une version simplifiée/alternative. Je pense que cette réponse devrait être acceptée. – Tomalak

+0

Utilisation très appropriée de - pour éviter d'avoir à passer des paramètres. J'approuve. :-) –

Questions connexes