I am trying to learn XSLT but I work best by example. I want to perform a trivial schema to schema transformation. How do I perform this transformation in only one pass (my current solution uses two passes and loses the original order of customers)?
From:
<?xml version="1.0" encoding="UTF-8"?>
<sampleroot>
<badcustomer>
<name>Donald</name>
<address>Hong Kong</address>
<age>72</age>
</badcustomer>
<goodcustomer>
<name>Jim</name>
<address>Wales</address>
<age>22</age>
</goodcustomer>
<goodcustomer>
<name>Albert</name>
<address>France</address>
<age>51</age>
</goodcustomer>
</sampleroot>
To :
<?xml version="1.0" encoding="UTF-8"?>
<records>
<record id="customer">
<name>Donald</name>
<address>Hong Kong</address>
<age>72</age>
<customertype>bad</customertype>
</record>
<record id="customer">
<name>Jim</name>
<address>Wales</address>
<age>22</age>
<customertype>good</customertype>
</record>
<record id="customer">
<name>Albert</name>
<address>France</address>
<age>51</age>
<customertype>good</customertype>
</record>
</records>
I already solved this a bad way (I lose the order of customers and I think that I have to parse the file multiple times:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/sampleroot">
<records>
<xsl:for-each select="goodcustomer">
<record id="customer">
<name><xsl:value-of select="name" /></name>
<address><xsl:value-of select="address" /></address>
<age><xsl:value-of select="age" /></age>
<customertype>good</customertype>
</record>
</xsl:for-each>
<xsl:for-each select="badcustomer">
<record id="customer">
<name><xsl:value-of select="name" /></name>
<address><xsl:value-of select="address" /></address>
<age><xsl:value-of select="age" /></age>
<customertype>bad</customertype>
</record>
</xsl:for-each>
</records>
</xsl:template>
</xsl:stylesheet>
Please can someone help me out with the correct XSLT construct where I only have to use a single parse (only one for-each)?
Thanks,
Chris
XSLT doesn't have any new function to search Strings in a reverse manner. We have substring function which creates two fields substring-before-last and substring-after-last.In XSLT it is defined as follows: <xsl:value-of select="substring (string name ,0, MAX_LENGTH )"/>...
Returns the contents of the current group selected by xsl:for-each-group. Available in XSLT 2.0 and later versions. Available in all Saxon editions. current-group() ➔ item()*
The following-sibling axis indicates all the nodes that have the same parent as the context node and appear after the context node in the source document.
It is a good XSLT practice to avoid using <xsl:for-each>
as much as possible.
Here is a simple solution, using this principle:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<records>
<xsl:apply-templates/>
</records>
</xsl:template>
<xsl:template match="badcustomer | goodcustomer">
<record>
<xsl:apply-templates/>
<customertype>
<xsl:value-of select="substring-before(name(), 'customer')"/>
</customertype>
</record>
</xsl:template>
</xsl:stylesheet>
Do note:
Only templates and <xsl:apply-templates>
are used.
The use of the identity rule and its overriding wherever necessary. This is one of the most fundamental XSLT design pattern.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With