This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: How can I solve this Global Variable Problem here


Hello Jia,

Thank you for including an example of your XML and XSL.  It makes identifying your problem a lot easier.

>Hi have a Global Variable Problem here.

The first thing that you need to grasp is that in XSL, global variables (i.e. variables that apply across templates) can only be set *outside* templates, at the top level.  In your XSL stylesheet, you are trying to set the variable 'weekday' within one template:

<xsl:template match="WeekDay">
	<xsl:variable name="weekday"><xsl:value-of
select="."/></xsl:variable>
	<xsl:apply-templates select="//WeekDayName" />
</xsl:template>

and then use it within another:

<xsl:template match ="WeekName">
	<xsl:if test="@name=$weekday">
		<xsl:value-of select="."/>
	</xsl:if>
</xsl:template>

You can't do that in XSL.  XSL templates can only know about variables that were set within themselves, passed to them as parameters, or set at the top level of the stylesheet (global variables).  If you want your WeekName-matching template to know about a variable called 'weekday', which changes each time it's called, you have to pass that to it as a parameter.  In your case, you would have to do it as:

<xsl:template match="WeekDay">
  <xsl:apply-templates select="//WeekDayName">
    <xsl:with-param name="weekday"><xsl:value-of select="."/></xsl:with-param>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="//WeekDayName">
  <xsl:param name="weekday" />
  <xsl:apply-templates select="WeekName">
    <xsl:with-param name="weekday" select="$weekday" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match ="WeekName">
  <xsl:param name="weekday" />
  <xsl:if test="@name=$weekday">
    <xsl:value-of select="."/>
  </xsl:if>
</xsl:template>

This works, but to be honest this general approach is a bit clumsy, and inefficient for two reasons.

Firstly, you are selecting any WeekDayName in the document - this is probably OK in your case because there's only one in the document anyway, but generally it's not efficient to use XPaths that match on // or something similar if you don't have to.  Also, remember that a node will only be processed if you select it - there's no need to specify that the WeedDayName-matching template matches '//WeekDayName' if the only route to that template is through a selection of nodes matching '//WeekDayName'.

Secondly, you are selecting all the WeekName elements, but only outputting something when it matches the relevant WeekName - it would *probably* be more efficient to only select the WeekNames that match in the first place (because XSL Processors probably deal with XPath predicates better that xsl:tests, though someone please correct me if I'm wrong).  That means you don't have to do the test (and don't have to pass the parameter) in the WeekName-matching template.

These two things really don't matter in your short example, but they might matter for someone who is using the same kind of principles in a longer or more complex situation.  I only bring them up for that reason.

Taking this into account, here is the second iteration on your XSL stylesheet:

<xsl:template match="WeekDay">
  <xsl:apply-templates select="/Test/WeekDayName">
    <xsl:with-param name="weekday"><xsl:value-of select="."/></xsl:with-param>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="WeekDayName">
  <xsl:param name="weekday" />
  <!-- only select the WeekName whose content is $weekday -->
  <xsl:apply-templates select="WeekName[@name=$weekday]" />
</xsl:template>

<xsl:template match ="WeekName">
  <xsl:value-of select="."/><xsl:text>
</xsl:text>
</xsl:template>

However, having said all this, it is usually the case that when you are trying to pinpoint a node in your input that is distinguished by some unique feature (in your case the 'name' attribute of your 'WeekName' elements), you could use xsl:key to good effect.  First, make a key that identifies those WeekName elements:

<xsl:key name="WeekNames" match="WeekName" use="@name" />

And then reference them using the key() function:

<xsl:template match="WeekDay">
  <xsl:value-of select="key('WeekNames', .)" />
</xsl:template>

So try the stylesheet:

----
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:key name="WeekNames" match="WeekName" use="@name" />

<xsl:template match="/">
	<xsl:apply-templates select="Test" />
</xsl:template>

<xsl:template match ="Test">
	<xsl:apply-templates select="DATA" />
</xsl:template>
<xsl:template match ="DATA">
	<xsl:apply-templates select="WeekDay" />
</xsl:template>

<xsl:template match="WeekDay">
  <xsl:value-of select="key('WeekNames', .)" />
</xsl:template>

</xsl:stylesheet>
----

[Tested and works in SAXON.  Note there are no spaces or line breaks between the day names, but you can add them using xsl:text if you want them.]

I hope that helps,

Jeni

Dr Jeni Tennison
Epistemics Ltd * Strelley Hall * Nottingham * NG8 6PE
tel: 0115 906 1301 * fax: 0115 906 1304 * email: jeni.tennison@epistemics.co.uk


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]