Korey Enright
Korey Enright

Reputation: 104

Use XSL to Iterate over Elements in For Each Loop

so I have been having serious issues getting this to work (and I'm sure it is simple, I have little experience with XSL).

I am trying to iterate over a bunch of very different child elements in an xml document that is structured like this:

<transaction>
 <data_xml>
  <document data_type="0">
   <element1>value1</element1>
   <element2>value2</element2>
   <element3>value3</element3>
  </document>
 </data_xml>
</transaction>

I am using XSL version 1.0, and I'm pretty sure that I cannot use 2.0 for this considering the constraints of the system I am working in.

The output I want is:

<foo>
 <bar:element1><!CDATA[[value]]></bar:element1>
 <bar:element2><!CDATA[[value]]></bar:element2>
 <bar:element3><!CDATA[[value]]></bar:element3>
</foo>

The awful hideous code I am currently using is:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
 <foo>
  <xsl:for-each select="/transaction/data_xml/document[@data_type='0']/*">
   &lt;bar:<xsl:value-of select="name(name(/transaction/data_xml/document[@data_type='0']/*)"/>>
   &#60;![CDATA[<xsl:value-of select="/transaction/data_xml/document[@data_type='0']/*"/>]]&gt;
   &#60;/bar:<xsl:value-of select="name(/transaction/data_xml/document[@data_type='0']/*)"/>>
  </xsl:for-each>
 </foo>
</xsl:template>
</xsl:stylesheet>

When I use this, though, it just iterates over the same element multiple times, instead of each element in turn.

Any ideas? Besides shooting me for terribad looking code?

Upvotes: 2

Views: 6222

Answers (2)

JLRishe
JLRishe

Reputation: 101652

Two things:

  • You can use name() or local-name() with no arguments to get the name of the current element.
  • You should be using xsl:element to create elements with dynamic names. You shouldn't be manually building their tags:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:bar="namespaceForBarGoesHere"
                version="1.0">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">
    <foo>
      <xsl:for-each select="/transaction/data_xml/document[@data_type='0']/*">
        <xsl:element name="bar:{local-name()}">
          <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
          <xsl:value-of select="." />
          <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
        </xsl:element>
     </xsl:for-each>
   </foo>
 </xsl:template>

Produces this result:

<foo xmlns:bar="namespaceForBarGoesHere">
  <bar:element1><![CDATA[value1]]></bar:element1>
  <bar:element2><![CDATA[value2]]></bar:element2>
  <bar:element3><![CDATA[value3]]></bar:element3>
</foo>

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 116959

You probably want to do something like:

XSLT 1.0

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

<xsl:template match="/">
    <foo>
        <xsl:for-each select="transaction/data_xml/document[@data_type='0']/*">
            <xsl:element name="bar:{local-name()}">
                <xsl:value-of select="."/>
            </xsl:element>
        </xsl:for-each>
    </foo>
</xsl:template>

</xsl:stylesheet>

which, applied to your example input, would result in:

<?xml version="1.0" encoding="utf-8"?>
<foo xmlns:bar="http://example.com/bar">
   <bar:element1>value1</bar:element1>
   <bar:element2>value2</bar:element2>
   <bar:element3>value3</bar:element3>
</foo>

If you want the various bar:elementX values to be CDATA sections, you should specify so in the <xsl:output> instruction so:

<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes" cdata-section-elements="bar:element1 bar:element2 bar:element3"/>

However this requires you to know all possible element names in advance. Otherwise you'd have to hack it, for example as:

<xsl:template match="/">
    <foo>
        <xsl:for-each select="transaction/data_xml/document[@data_type='0']/*">
            <xsl:element name="bar:{local-name()}">
                <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
                <xsl:value-of select="."/>
                <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
            </xsl:element>
        </xsl:for-each>
    </foo>
</xsl:template>

Upvotes: 2

Related Questions