Home > Blockchain >  xsl template match using external parameter as variable
xsl template match using external parameter as variable

Time:01-04

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>
  • Related