Re: [xsl] Variable in XPath

Subject: Re: [xsl] Variable in XPath
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Wed, 27 Jun 2007 17:39:15 +0200
Hi Garvin,

see my comments:


Garvin Riensche wrote:
Hello,

I hope that anyone can help me with the following. I want to write a stylesheet that is used to check if an element (class) with a special ID which is given as variable from the commandline exists in the source or not. If no variable is supplied I want to test if there are "class" nodes at all. Of course it's no problem to check if a node with a special ID exists or not. But what do I use as default value of the variable so that all "class" nodes are selected if no variable is supplied.

?


<xsl:variable name="id" select="attribute::*"/>

Why a variable? You wanted to set it externally, in which case you better use a parameter.


Seems that you want the variable to hold the value of the current attribute by default? This only works when there's a context item, and there is no context item on the root level of a stylesheet, so this effectively means that your variable contains the empty sequence.

You could provide an absolute xpath, but then you would loose the context. I suppose this is cleaner instead and gives you all you need:

<xsl:param name="id" />


<xsl:template match="//condition/and-condition">

It is best practice (most of the time) *not* to use // in a match clause, it serves no purpose.



<xsl:when test="doc('factbase.xml')/facts/class[@id eq $id]">


The test should be true if $id is set to "2" or "3"

$id = (2, 3)


and if $id is not set at all.

assuming 'and' is semantically 'or' here (because $id = (2, 3) and empty($id) is never true):


$id = (2,3) or empty($id)

So, the key is the comparison "@id eq $id" and the default value of $id.

ah, you meant something else?....


"attribute::*" as default value is obviously wrong.

see above, it will give an empty sequence


Which default value do I have to use to test if class nodes exist at all?

I don't see why you want a default value for this, just don't use the $id:


empty(facts/class)

or, if you do some xsl:apply-templates, you probably don't have to worry about non-existing nodes at all (over-use of xsl:choose/when/if is a common cause of unstructured hard-to-maintain code):

<xsl:apply-templates select="facts/class" />

will not select 'class' when there is none.


Now that we put it all together, I assume you mean something like: if $id matches one or more @id, do something, if it matches none, but there are class-nodes, do something else, if there are no class-nodes, do something else. Here's one way to do it (in this case without xsl:choose, but you can of course use that if you feel more comfortable about it):


<xsl:param name="id" />

<xsl:template match="your-start">
   <xsl:apply-templates match="doc('factbase.xml')/facts/class" />
</xsl:template>

<xsl:template match="class[@id = $id]">
   ...do your stuff with a matching $id=@id for class-nodes...
</xsl:template>

<xsl:template match="facts[not(class/@id = $id)]/class">
...do your stuff when no @id matches the supplied $id, but class-nodes exist...
</xsl:template>


<xsl:template match="facts[empty(class)]">
  ...do your stuff when there are no class-nodes...
</xsl:template>


Happy coding!


Cheers,
-- Abel Braaksma



PS: alternatively, you could remove the second xsl:template match rule with the following, which reads easier and works equally well, because of priority rules:

<xsl:template match="class">
...do your stuff when no @id matches the supplied $id, but class-nodes exist...
</xsl:template>


Current Thread