XSL Match Element based on a Value
I am transforming an XML where I am supposed to locate a particular Element (based on the attribute value) and update the Element and its child attributes.
The sample XML file is as below.
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<Environments>
<Environment id="Master"/>
<Environment id="Developer"/>
</Environments>
<Common>
<Logging>
<LogFile>log\updater.log</LogFile>
</Logging>
</Common>
<Configuration>
My XSLT file is as below.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:param开发者_开发知识库 name="EnvironmentId" />
<xsl:param name="SelectEnvironment" />
<!-- Copy All Elements -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Modify Element with id = Developer-->
<xsl:template match="Environment/@id[. ='Developer']">
<xsl:attribute name="id">
<xsl:value-of select="$EnvironmentId"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
In this XSLT, variable EnvironmentId contains the new id; variable SelectEnvironment should contain the value Developer (or any other user provided value passed via C#.NET)
Question
How do I write my XSLT so that the match works based on a user-defined value?
I tried the following
<xsl:template match="Environment/@id[. ='$SelectEnvironment']">
<xsl:attribute name="id">
<xsl:value-of select="$EnvironmentId"/>
</xsl:attribute>
</xsl:template>
No errors. But, the attr id was not updated.
I tried this...
<xsl:template match="Environment/@id[. =$SelectEnvironment]">
<xsl:attribute name="id">
<xsl:value-of select="$EnvironmentId"/>
</xsl:attribute>
</xsl:template>
And I got a run time error of Variables cannot be used within this expression.
You cannot have variables in template matches; they can be compared to compile-time. The answer to your conundrum is to move the logic out of the match (and perhaps lose a slight penalty in performance). Untested ;
<xsl:template match="Environment/@id">
<xsl:if test="[.=$SelectEnvironment]">
<xsl:attribute name="id">
<xsl:value-of select="$EnvironmentId"/>
</xsl:attribute>
</xsl:if>
</xsl:template>
However, there's other, better and faster ways to slice your problem which probably can be explained a bit better than what your example problem points to?
Here's my full XSLT that does what you want, fully tested (Is the missing closing of the element a mistake in copy-paste?) ;
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:param name="EnvironmentId" />
<xsl:param name="SelectEnvironment" />
<!-- Copy All Elements -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Modify Element with id = Developer-->
<xsl:template match="Environment">
<xsl:choose>
<xsl:when test="@id=$SelectEnvironment">
<Environment id="{$EnvironmentId}" />
</xsl:when>
<xsl:otherwise>
<Environment id="{@id}" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I tried this...
<xsl:template match="Environment/@id[.
=$SelectEnvironment]">
<xsl:attribute name="id">
<xsl:value-of select="$EnvironmentId"/>
</xsl:attribute>
</xsl:template>
And I got a run time error of Variables cannot be used within this expression.
In XSLT 1.0 a match expression cannot contain a reference to a variable or to a parameter. This was done with the intention to prevent circular definitions.
However in XSLT 2.0 they are allowed in a match pattern.
Therefore, you can do so using XSLT 2.0.
For some people XSLT 2.0 is still a distant dream -- then you can always do as recommended in the accepted answer.
精彩评论