views:

43

answers:

2

Hello...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.

+2  A: 

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>
AlexanderJohannesen
@AlexanderJohannesen: Thanks! My XSLT skills are limited (just 1 day old!), in the second line of your code...you are closing the <xsl:if>; why would you want to close it again as the last but one line? In addition, copy-paste of this code does not work even if I remove the closing of xsl:if command. Any thing that needs to be modified.
Kanini
Sorry, the first <xsl:if ...> should not have been closed. Fixed it in the code example, and added final example.
AlexanderJohannesen
@AlexanderJohannesen: Sorry to be a pain. Your complete XSLT does not select the Environment based on the variable, but picks up the Environment which has id value = Developer. Maybe a wrong copy-paste? Second, I am not sure which missing colon you are referring to. Third, the modified XSLT where we remove the <xsl:if ...> does not work either. It throws an error *Unexpected Token '[' in the expression*
Kanini
Sorry, yes, bad copy'n'paste. Updated the example, and it does what you want, although I doubt this is a very good solution. It would be better to explain what the problem space really is, and see if there's a better way to approach it.
AlexanderJohannesen
+1 Right solution for XSLT 1.0
Alejandro
@AlexanderJohannesen: Your answer is correct only for the obsolete version of XSLT -- 1.0. Please, do not label statements generically as "in XSLT". Instead say: "In XSLT 1.0". All this said, yes, this is a very good answer.
Dimitre Novatchev
Dimitre Um, XSLT 1.0 is *NOT* obsolete; it is still the most used version, embedded almost everywhere you go, including most browsers. XSLT 2.0 have had a terrible adoption rate, and there's not that many 2.0 processors out there (I can think of a small handfull, Saxon as always leading the way)
AlexanderJohannesen
Dimitre, I might also add that the stylesheet from the OP specifies version 1.0, so giving a 2.0 answer would be outright wrong.
AlexanderJohannesen
+1  A: 

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.

Dimitre Novatchev
+1 Update, update, update...
Alejandro