I would like to transform the following xml:
<root>
<row id="1">
<order>4711</order>
<customer>a</customer>
</row>
<row id="3">
<order>4712</order>
<customer>a</customer>
</row>
<row id="4">
<order>4713</order>
<customer>b</customer>
</row>
<row id="5">
<order>4714</order>
<customer>c</customer>
</row>
</root>
...into the xml below, by dynamically filtering on the value of customer using external parameters
<root>
<row id="1">
<order>4711</order>
<customer>a</customer>
</row>
<row id="3">
<order>4712</order>
<customer>a</customer>
</row>
</root>
I managed to construct the following xsl and the result is as expected, if I transfer the param as filterAt=customer and filterVal=a:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="filterVal"/>
<xsl:param name="filterAt"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row">
<xsl:if test="*[local-name()=$filterAt]=$filterVal">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
However, I am wondering whether I can also make the template-match argument as an external parameter. My current argument of match is "row". I would like to pass a third parameter called "rowName" and dynamically use it in the match. Is there a way to achieve this?
I tried the following but xmlspy won't even allow it:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="filterVal"/>
<xsl:param name="filterAt"/>
<xsl:param name="rowName"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="$rowName">
<xsl:if test="*[local-name()=$filterAt]=$filterVal">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Thank you.
CodePudding user response:
In XSLT 1.0, template patterns may not use variables. But you can express the condition in an <xsl:when>
element:
<xsl:template match="*" priority="1">
<xsl:choose>
<xsl:when test="local-name()=$rowName">
<xsl:if test="*[local-name()=$filterAt]=$filterVal">
<xsl:copy-of select="." />
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Noteworthy points:
- The template has priority 1 so that it takes precedence over the
<xsl:template match="node()|@*">
when an element node matches both. - The
<xsl:otherwise>
duplicates the content of the<xsl:template match="node()|@*">
template.
CodePudding user response:
In XSLT 2 and later you can use e.g. match="*[local-name() = $filterAt and . = $filterVal"
or perhaps even easier with an empty template to not copy the non matching elements e.g. <xsl:template match="*[local-name() = $filterAt][not(. = $filterVal)]"/>
.
Also in XSLT 3, the current version of XSLT, there are static parameters and shadow attributes (notice the _match
) e.g.
<xsl:param name="row-name" as="xs:string" static="yes" select="'row'"/>
<xsl:template _match="{$row-name}">...</xsl:template>