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']/*">
<bar:<xsl:value-of select="name(name(/transaction/data_xml/document[@data_type='0']/*)"/>>
<![CDATA[<xsl:value-of select="/transaction/data_xml/document[@data_type='0']/*"/>]]>
</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?
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"><![CDATA[</xsl:text>
<xsl:value-of select="."/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:element>
</xsl:for-each>
</foo>
</xsl:template>
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"><![CDATA[</xsl:text>
<xsl:value-of select="." />
<xsl:text disable-output-escaping="yes">]]></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>