Pairing Off in XSLT
Pairing Off in XSLT
My friend Newton was just fighting to transform some XML that was structured in a awkward way. The machine generated XML he was working with basically paired off successive elements, for example to provide a credential prompt and then the following element contained the credential itself. It took a bit of fiddling to get the right XPath expression to perform the transformation. I thought it would be illuminating to share it with you and hopefully someone will find a better solution than the one we came up with.The Source
The source XML looked like this<?xml version="1.0" encoding="UTF-8" ?>
<exampleElement>
<AttributeElement>
<DisplayElement>
<SelectElement attribute1="A"/>
</DisplayElement>
</AttributeElement>
<AttributeElement>
<DisplayElement>
<PasswordElement attribute1="1"/>
</DisplayElement>
</AttributeElement>
...
<AttributeElement>
<DisplayElement>
<SelectElement attribute1="E"/>
</DisplayElement>
</AttributeElement>
<AttributeElement>
<DisplayElement>
<PasswordElement attribute1="5"/>
</DisplayElement>
</AttributeElement>
</exampleElement>
Note the pairing of the Attribute elements.<exampleElement>
<AttributeElement>
<DisplayElement>
<SelectElement attribute1="A"/>
</DisplayElement>
</AttributeElement>
<AttributeElement>
<DisplayElement>
<PasswordElement attribute1="1"/>
</DisplayElement>
</AttributeElement>
...
<AttributeElement>
<DisplayElement>
<SelectElement attribute1="E"/>
</DisplayElement>
</AttributeElement>
<AttributeElement>
<DisplayElement>
<PasswordElement attribute1="5"/>
</DisplayElement>
</AttributeElement>
</exampleElement>
The Target
The target XML looked like this<?xml version = '1.0' encoding = 'UTF-8'?>
<exampleElement>
<Attribute>
<Select attribute1="A"/>
<Password attribute1="1"/>
</Attribute>
...
<Attribute>
<Select attribute1="E"/>
<Password attribute1="5"/>
</Attribute>
</exampleElement>
<exampleElement>
<Attribute>
<Select attribute1="A"/>
<Password attribute1="1"/>
</Attribute>
...
<Attribute>
<Select attribute1="E"/>
<Password attribute1="5"/>
</Attribute>
</exampleElement>
The Problem
The challenge was after identifying a path with a SelectElement element how to select the following path that had a PasswordElement element.The Answer is Axis
The trouble with XSLT is that I don't do enough of it to remember all the tools available. In this case it was very simple once I had remembered how axis work. Here is my solution<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<exampleElement>
<xsl:for-each select="/exampleElement/AttributeElement/DisplayElement/SelectElement">
<Attribute>
<Select>
<xsl:attribute name="attribute1"><xsl:value-of select="@attribute1"/></xsl:attribute>
</Select>
<Password>
<xsl:attribute name="attribute1">
<xsl:value-of select="following::AttributeElement[1]/DisplayElement/PasswordElement/@attribute1"/>
</xsl:attribute>
</Password>
</Attribute>
</xsl:for-each>
</exampleElement>
</xsl:template>
</xsl:stylesheet>
This solution iterates over all the SelectElement elements and then for each of those elements it picks out the following PasswordElement in the document. It does this by using the XPath expression "following::AttributeElement[1]/DisplayElement/PasswordElement/@attribute1".<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<exampleElement>
<xsl:for-each select="/exampleElement/AttributeElement/DisplayElement/SelectElement">
<Attribute>
<Select>
<xsl:attribute name="attribute1"><xsl:value-of select="@attribute1"/></xsl:attribute>
</Select>
<Password>
<xsl:attribute name="attribute1">
<xsl:value-of select="following::AttributeElement[1]/DisplayElement/PasswordElement/@attribute1"/>
</xsl:attribute>
</Password>
</Attribute>
</xsl:for-each>
</exampleElement>
</xsl:template>
</xsl:stylesheet>
The "following::" axis selects all nodes in the document that come after the given SelectElement element. This flattens the hierarchy enabling us to choose the next AttributeElement element by selecting the AtrributeElement element with index 1 (AttributeElement[1]). We can then traverse that part of the XML using the normal child axis. Simple when its done but when you rarely use axis other than the child axis it takes a while to remember how they work.
Reference
I find the two O'Reilly books on XSLT to be very helpful when struggling with XPath and XSLT. These are the Doug Tidwell XSLT book and the Sal Mangano XSLT Cookbook.I hope this saves sombody some time.