Issue with processing XSLT having multiple conditions
-
30-05-2021 - |
Question
My input XML has the following structure:
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
<attributes>
<attribute>
<key>1</key>
<value>one</value>
</attribute>
<attribute>
<key>2</key>
<value>two</value>
</attribute>
</attributes>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
<attributes>
<attribute>
<key>1</key>
<value>one</value>
</attribute>
<attribute>
<key>2</key>
<value>two</value>
</attribute>
</attributes>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
<attributes>
<attribute>
<key>1</key>
<value>one</value>
</attribute>
<attribute>
<key>2</key>
<value>two</value>
</attribute>
</attributes>
</cd>
<cd>
<title>Still got the blues</title>
<artist>Gary Moore</artist>
<country>UK</country>
<company>Virgin records</company>
<price>10.20</price>
<year>1990</year>
<attributes>
<attribute>
<key>1</key>
<value>WON</value>
</attribute>
<attribute>
<key>2</key>
<value>two</value>
</attribute>
</attributes>
</cd>
</catalog>
Now, I am trying to create a different xml by selecting only those cd nodes for which { key = 1 and value = WON } (Value node which is sibling of that key node). Been stuck on this for a while, trying to apply multiple conditions. Tried following approaches: 1. Try to copy those nodes that match conditions 2. Doing an identity copy & ignoring those nodes that do not match condition
Not sure if this is feasible, or I am doing something wrong. Here is what my xslt looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"
indent="yes"/>
<xsl:variable name="KeyToBeMatched">1</xsl:variable>
<xsl:param name="ValueToBeMatched">WON</xsl:param>
<xsl:template match="catalog">
<xsl:for-each select="cd">
<xsl:for-each select="attributes/attribute[keu = $KeyToBeMatched]">
<xsl:variable name="attributeValue" select="value"/>
<xsl:if test="$attributeValue = $RMGAccountId">
<xsl:copy>
<xsl:apply-templates select="@*|*|text()" />
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Have tried other combinations too. Would really appreciate any help on this.
Thanks, Ani
Solution
I am not sure if you want to copy the whole cd element or not should it have a matching attribute, but if you did, you could simply add a matching template like this, in which you then add code to copy the element
<xsl:template match="cd[attributes/attribute[key='1'][value='WON']]">
<!-- Copy element -->
</xsl:template>
Other cd elements can then be matched and ignored
<xsl:template match="cd" />
Try this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="cd[attributes/attribute[key='1'][value='WON']]">
<xsl:call-template name="identity" />
</xsl:template>
<xsl:template match="cd" />
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, out the late, great Gary Moore is output
<catalog>
<cd>
<title>Still got the blues</title>
<artist>Gary Moore</artist>
<country>UK</country>
<company>Virgin records</company>
<price>10.20</price>
<year>1990</year>
<attributes>
<attribute>
<key>1</key>
<value>WON</value>
</attribute>
<attribute>
<key>2</key>
<value>two</value>
</attribute>
</attributes>
</cd>
</catalog>
Of course, in your case, you need to parameterise the matching values, in which case you can't use this approach, because variables can't be use in the template match. Instead, you could use a xsl:for-each to match the cd elements:
<xsl:for-each select="cd[attributes/attribute[key=$KeyToBeMatched][value=$ValueToBeMatched]]">
Here is the full XSLT for this approach
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="KeyToBeMatched">1</xsl:variable>
<xsl:param name="ValueToBeMatched">WON</xsl:param>
<xsl:template match="catalog">
<xsl:for-each select="cd[attributes/attribute[key=$KeyToBeMatched][value=$ValueToBeMatched]]">
<xsl:call-template name="identity"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This will also return the awesome Gary Moore.
OTHER TIPS
try this one:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="KeyToBeMatched">1</xsl:variable>
<xsl:param name="ValueToBeMatched">WON</xsl:param>
<xsl:template match="list">
<xsl:for-each select="cd">
<xsl:for-each select="attributes/attribute[key = $KeyToBeMatched]">
<xsl:variable name="attributeValue" select="value"/>
<xsl:if test="$attributeValue = $ValueToBeMatched">
<xsl:copy>
<xsl:apply-templates select="@*|*|text()" />
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Variable "RMGAccountId" has never been declared, I exchanged that with "ValueToBeMatched". and also fixed the typo that Filype pointed out. Applied to the source you get this XML:
<?xml version="1.0" encoding="UTF-8"?>
<attribute> 1 WON </attribute>
Best regards, Peter
There's a typo on
--------------------------------------------V---------------------
<xsl:for-each select="attributes/attribute[keu = $KeyToBeMatched]">