Selection

1. Select and match
2. Using parameter with xsl:for-each
3. Invalid predicate
4. footnotes
5. Unknown element selection (parent is known)
6. Selecting only specific elements
7. Determining if the first child is a specific element
8. Unique element values only
9. XPath expression for every child but one
10. How to check is element has children
11. Pruning nodes not required
12. Pruning nodes not in xpath list
13. Selecting only one node value out of the many identical values
14. Selecting all but two elements by name
15. Get the second para after the heading
16. Select all children except ...
17. Select an element based on childrens name
18. Selecting elements in a given namespace
19. Selective processing of first n elements
20. Pick first few elements as file sample
21. Select one from a number of items
22. Namespace nodes.

1.

Select and match

Mike Brown

Certain instructions have a select attribute, others have a match attribute, but never both. You "select" a set of nodes that you want to process. You declare that a template "matches" nodes that have a certain pattern.

An XSLT processor starts at the root node and finds the template that best matches that node. It executes the instructions in that template. Then it is done. An apply-templates instruction in that one template tells the processor to select another set of nodes and, for each of those nodes, find the template that best matches it and execute the instructions in it. Many templates may match a given node; there are rules in the spec for determining which template will be chosen.

Templates are not executed in a specific order. Templates only say "if the XSLT processor decides that this template provides the best match for the node *that it is already looking at*, here are some instructions to execute." They are like subroutines, whereas the main program is hidden inside the XSLT processor.

2.

Using parameter with xsl:for-each

David Carlisle

you can't in standard xsl evaluate a string as an xpath expresion.

you have <xsl:value-of select="$selectpath"/>

but you passed it a string: "Resources/Resource" so it is equivalent to <xsl:value-of select="'Resources/Resource'"/>

Mike Brown expands: To restate what David Carlisle was saying, if an expression that identifies a node-set is expected, the only kind of variable you can use in the expression is one that is itself a node-set. You can't use a string that resembles an expression that, if evaluated as an expression, would return a node-set.

So you can never define a string like 'foo/bar' and then put that in an expression like /path/to/$yourstring/and/beyond. You can, however, use non-node-set variables in predicates. So if you had, say, variable $x that was the string 'foo', /path/to/*[name()=$x] wouldn't be out of the question.

3.

Invalid predicate

Gary L Peskin

Why is it that a predicate is not allowed when I do something like

<xsl:variable name="foo" select=".[@id=1]"/>

I mean if the current node has an 'id' attribute numerically equal to the number 1, then $foo will be the current node; otherwise it will be an empty node-set, right? Both SAXON and XT gripe at me for having a predicate after . at all. I can't even say ".[true()]"! What's the deal? I don't see anything in XPath prohibiting this.

Try self::node()[@id=1]

The "." is an AbbreviatedStep (production [12]) and cannot be followed by a predicate according to production [4]. That production requires

	AxisSpecifier NodeTest Predicate*

so "." is not just self::node(), it is an abbreviation of the *step* self::node() -- axis, node test, and any predicates inclusive. Makes sense, then, but isn't obvious by the prose portion of the XPath spec.

Mike Kay adds:

"." is an abbreviated step, not an abbreviated the-part-of-the-step-before the-predicates. Why that should be, I don't know, but that's what XPath says.

4.

footnotes

Jeni Tennison


>What I need to do is to process all FN elements located prior to the current
><P> element, BUT after the last set of <FN> elements I already processed!
    

I guess that your XML looks something like:

<doc>
  <FN id="fn1">
    <P>Footnote 1</P>
  </FN>
  <P id="para1">Paragraph 1</P>
  <section>
    <FN id="fn2">
      <P>Footnote 2</P>
    </FN>
    <H1>Heading</H1>
    <P id="para2">Paragraph 2</P>
  </section>
</doc>

In other words: (a) FNs are always associated with (and processed alongside) Ps, not with any other elements (b) FNs may not immediately precede their respective P

If this is the case, given that your current node is a P element, you can get the FN elements between it and the previous P using:

preceding::FN[generate-id(following::P[1]) = 
                         generate-id(current())]
In other words:
any FN element that precedes the current element in the source document 
  such that:
    the unique ID for the immediately following P of the FN is the same as
      the unique ID of the current (P) element

[You don't have to generate-id()s if you have some other natural identifier for the P elements, like an 'id' attribute.]

So, you could probably use something like:

<xsl:template match="P">
  <xsl:apply-templates />
  <xsl:apply-templates 
     select="preceding::FN[generate-id(following::P[1]) = 
                                             generate-id(current())]" />
</xsl:template>

<xsl:template match="FN">
  <p /><xsl:value-of select="P" />
</xsl:template>

The reason I've done away with the 'Dump Footnotes' named template was that it's not really necessary: you can just select the FNs that you want to process within the 'P'-matching template as above rather than passing them as a parameter to a template. Of course you can change it back if you want to :)

5.

Unknown element selection (parent is known)

Oliver Becker


> How do I select a record without knowing the
> name of it. What I know is the name of the parent,
> 
> <PARENT>
>     <???>Donald Ball</???>
>     <???>23</???>
> </PARENT>
	

PARENT/* selects all children of the PARENT element

PARENT/*[1] selects only the first child

> Another thing i do not know is the number of elements. 

count(PARENT/*)

6.

Selecting only specific elements

Miloslav Nic

Source

<a>
 <b>some text</b>
 <b>some more text</b>
 <b>some other text</b>
 <b>some last text</b>
<c>some c text</c>
</a>

Wanted output

<a>
 <bset>
  <b>some text</b>
  <b>some more text</b>
  <b>some other text</b>
  <b>some last text</b>
 </bset>
<c>some c text</c>
</a>

Try

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

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

<xsl:template match="b">
    <xsl:if test="not(name(preceding-sibling::*[1])='b')">
      <bset>
	<xsl:copy-of select="."/>
	<xsl:apply-templates select="following-sibling::*[1]" mode="set"/>
      </bset>      
    </xsl:if>
</xsl:template>

<xsl:template match="b" mode="set">
  <xsl:copy-of select="."/>
  <xsl:apply-templates select="following-sibling::*[1]" mode="set"/>
</xsl:template>

<xsl:template match="*" mode="set"/>

<xsl:template match="*">
  <xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>

It needs some more work to make it really general, but I hope it will give you some ideas.

7.

Determining if the first child is a specific element

David Carlisle


> I need to know if the first child of a node is an
> element (para, in this case).  The test needs to be done in the parent's
> <template> element, prior to an <apply-templates/>.  Is there a
> straightforward <if> test I can use for this case?
	
<xsl:if test="*[1][self::para]">David</xsl:if>

Jeni Tennison expands:

It's not clear whether you want to know whether it's a specific element (a para element) or an element rather than some other type of node (e.g. text or a comment).

You can get the first child node using the XPath:

  child::node()[1]

You can get the first child element using the XPath:

  child:*[1]

You can test whether something is an element using the test:

  self::*

You can test whether something is a para element using the test:

  self::para

So, you can find the first child node that is an element using the
XPath:

  child::node()[1][self::*]

If the first child node is not an element, then no nodes will be selected with this XPath. Within an xsl:if test, a node set with no nodes in it evaluates as false() while one that does hold nodes evaluates as true(). So, to test whether the first child node of the current node is an element, use:

  <xsl:if test="child::node()[1][self::*]">
     ...
  </xsl:if>

Mike Brown cautions here

Also be careful in this case that you have accounted for the possibility of any whitespace-only text nodes. So, for example, if your document read:

  <CHAPTER>
     <para>Here's my first child element, a para.</para>
  ...
  </CHAPTER>

Jeni's test above will fail on account of the whitespace-only text node preceding the para element (it contains a carriage return and two space characters).

  Adjust for this by saying
  
  <xsl:strip-space elements="CHAPTER"/>
  
  at the top level, or altering your test (somewhat horribly) to:
  
  <xsl:if test="child::node()[not(.=normalize-space(.))][1][self::para]">
      ...
  </xsl:if>

Jeni Continues:

To test whether the first child element of the current node is a para element, use:

  <xsl:if test="child::*[1][self::para]">
     ...
  </xsl:if>

Wendell Piez wraps it up:

But of course after seeing Mike Kay's nice test in another thread, I realized a slightly more elegant and efficient version would be

<xsl:if test="child::node()[normalize-space(.)][1][self::para]">
    ...
</xsl:if>

...which reveals further complications (funny how these things are). We have to make sure it's skipped only if it's a whitespace-only _text_ node (not, say, an empty element node). So....

<xsl:if test="child::node()[not(self::text()) or 
normalize-space(.)][1][self::para]">
    ...
</xsl:if>

The first predicate evaluates to true if the child node is not a text node or if it has a non-whitespace string value. The second chooses the first of these. The third checks to see whether it's a para element.

For evident reasons, that <xsl:strip-space .../> at the top level will be preferable, if you can use it.

8.

Unique element values only

Jeni Tennison


> I've tried manipulating the order of the ACHFee element with
> <xsl:sort select="feeLevelBottom"/> and so-on. I've used every
> combination of preceding::, following::, following-sibling:: that I
> can come up with, but I still cannot make ONLY UNIQUE cell contents
> be displayed

Here's what you're missing: axes *always* operate on the structure of the original XML, it don't matter how much you sort or what axes you try.

At the moment you trying to do (roughly)

  for each ACHfee
    sorted according to feeLevelBottom
       if you haven't already processed something with the same
             feeLevelBottom
          then process this one

Instead, you need to turn it around so that you only select the first ACHfees with a particular feeLevelBottom in the first place, iterate over them, and sort *them* according to the feeLevelBottom.

For example:

<xsl:for-each select="//ACHFee
                       [not(feeLevelBottom =
                            preceding::ACHFee/feeLevelBottom)]">
   <xsl:sort select="feeLevelBottom" />
   ...
</xsl:for-each>

You may find it more efficient to use the Muenchian Method to get the list of fees with unique feeLevelBottoms. Define a key:

<xsl:key name="fees"
         match="ACHFee"
         use="number(feeLevelBottom)" />

And then select the ACHFees with unique feeLevelBottom values with:

<xsl:for-each
   select="//ACHFee
             [count(.|key('fees', number(feeLevelBottom))[1]) = 1]">
   <xsl:sort select="feeLevelBottom" />
   ...
</xsl:for-each>

9.

XPath expression for every child but one

Francis Norton and Jeni Tennison


>  Is it possible to have an XPath expression that means "every child but one"
>  E.g. every child except <title>
	

Yes, I think this came up fairly recently - the other solution is a bit non-obvious:

	<xsl:apply-templates select="*[not(self::title)]"/>

Jeni adds

Or, generally better because it avoids namespace problems:

  <xsl:apply-templates select="*[not(self::title)]" />

10.

How to check is element has children

Michael Kay

I need to test if an element has children

Assuming it is the current element:

xsl:if test="node()" for any children, or

xsl:if test="*" for element children.

11.

Pruning nodes not required

Jeni Tennison


> I would like to be able to generate a stylesheet that will produce
> output documents that exclude all nodes that are not matched by any
> of the xpath expressions.
	

The first thing is to work out a stylesheet that does what you want with the expressions hard-coded into it...

> From an algorithm point of view, one solution is to create a
> node-set collection of all nodes that should be copied to the output
> document. 

OK, so first work out which nodes have been selected by the expressions:

<xsl:variable name="selected-nodes"
              select="//product/@sku | //product/cost"/>

Then to work out which nodes should therefore be copied - that's all those nodes that are selected, plus their ancestors:

<xsl:variable name="copied-nodes"
              select="$selected-nodes | $selected-nodes/ancestor::*"/>

Now, starting from the top (the document element is always the first in the list of copied nodes), we want to work through the document. If the element is in the list of selected nodes, then we just want to copy it completely. Otherwise, we want to copy the element, copy any attributes that are in the list of selected nodes, and apply templates to any of its children that are in the list of nodes to be copied.

Now, working out whether a node is in a set of nodes involves a bit of set manipulation - if you count how many nodes there are in the set that's the union of the node and the node set, and it's the same as the number of nodes in the node set, then the node has to be in the node set. So to work out whether the context node is in the set of $selected-nodes, I can use:

  count(.|$selected-nodes) = count($selected-nodes)

Since $selected-nodes and $copied-nodes are known up front, I'll create variables that indicate how many nodes are in each of them:

<xsl:variable name="nselected-nodes" select="count($selected-nodes)" />
<xsl:variable name="ncopied-nodes" select="count($copied-nodes)"/>

So the template looks like:

<xsl:template match="*">
   <xsl:choose>
      <!-- this node is one of the selected nodes -->
      <xsl:when test="count(.|$selected-nodes) = $nselected-nodes">
         <xsl:copy-of select="."/>
      </xsl:when>
      <xsl:otherwise>
         <xsl:copy>
            <!-- copy attributes that are selected nodes -->
            <xsl:copy-of select="@*[count(.|$selected-nodes) =
                                    $nselected-nodes]"/>
            <!-- apply templates to nodes that are to be copied -->
            <xsl:apply-templates select="node()[count(.|$copied-nodes) =
                                                $ncopied-nodes]"/>
         </xsl:copy>
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

And there you have it. That stylesheet will take the input you specified and give the output you specified.

Now all you have to do is create a stylesheet that creates it. In fact most of it is static, so you could bundle it out to a separate stylesheet and include it rather than create it dynamically. The only thing that isn't static is the creation of the $selected-nodes variable:

<xsl:variable name="selected-nodes"
              select="//product/@sku | //product/cost"/>

Assuming that you're using oxsl as the namespace prefix for your XSLT namespace alias, then you need to create an oxsl:variable with a name attribute equal to selected-nodes:

<oxsl:variable name="selected-nodes">
   ...
</oxsl:variable>

The select attribute is created dynamically by iterating over the various expressions that you have (I'll assume their held in an $exprs variable):

<oxsl:variable name="selected-nodes">
   <xsl:attribute name="select">
      <xsl:for-each select="$exprs">
         <xsl:value-of select="." />
         <xsl:if test="position() != last()"> | </xsl:if>
      </xsl:for-each>
   </xsl:attribute>
</oxsl:variable>

You should be able to create the rest of the stylesheet very straight-forwardly.

12.

Pruning nodes not in xpath list

Jeni Tennison


> I would like to be able to generate a stylesheet that will produce
> output documents that exclude all nodes that are not matched by any
> of the xpath expressions.

The first thing is to work out a stylesheet that does what you want with the expressions hard-coded into it...

> From an algorithm point of view, one solution is to create a
> node-set collection of all nodes that should be copied to the output
> document. 

OK, so first work out which nodes have been selected by the expressions:

<xsl:variable name="selected-nodes"
              select="//product/@sku | //product/cost"/>

Then to work out which nodes should therefore be copied - that's all those nodes that are selected, plus their ancestors:

<xsl:variable name="copied-nodes"
              select="$selected-nodes | $selected-nodes/ancestor::*"/>

Now, starting from the top (the document element is always the first in the list of copied nodes), we want to work through the document. If the element is in the list of selected nodes, then we just want to copy it completely. Otherwise, we want to copy the element, copy any attributes that are in the list of selected nodes, and apply templates to any of its children that are in the list of nodes to be copied.

Now, working out whether a node is in a set of nodes involves a bit of set manipulation - if you count how many nodes there are in the set that's the union of the node and the node set, and it's the same as the number of nodes in the node set, then the node has to be in the node set. So to work out whether the context node is in the set of $selected-nodes, I can use:

  count(.|$selected-nodes) = count($selected-nodes)

Since $selected-nodes and $copied-nodes are known up front, I'll create variables that indicate how many nodes are in each of them:

<xsl:variable name="nselected-nodes" select="count($selected-nodes)" />
<xsl:variable name="ncopied-nodes" select="count($copied-nodes)"/>

So the template looks like:

<xsl:template match="*">
   <xsl:choose>
      <!-- this node is one of the selected nodes -->
      <xsl:when test="count(.|$selected-nodes) = $nselected-nodes">
         <xsl:copy-of select="."/>
      </xsl:when>
      <xsl:otherwise>
         <xsl:copy>
            <!-- copy attributes that are selected nodes -->
            <xsl:copy-of select="@*[count(.|$selected-nodes) =
                                    $nselected-nodes]"/>
            <!-- apply templates to nodes that are to be copied -->
            <xsl:apply-templates select="node()[count(.|$copied-nodes) =
                                                $ncopied-nodes]"/>
         </xsl:copy>
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

And there you have it. That stylesheet will take the input you specified and give the output you specified.

Now all you have to do is create a stylesheet that creates it. In fact most of it is static, so you could bundle it out to a separate stylesheet and include it rather than create it dynamically. The only thing that isn't static is the creation of the $selected-nodes variable:

<xsl:variable name="selected-nodes"
              select="//product/@sku | //product/cost"/>

Assuming that you're using oxsl as the namespace prefix for your XSLT namespace alias, then you need to create an oxsl:variable with a name attribute equal to selected-nodes:

<oxsl:variable name="selected-nodes">
   ...
</oxsl:variable>

The select attribute is created dynamically by iterating over the various expressions that you have (I'll assume their held in an $exprs variable):

<oxsl:variable name="selected-nodes">
   <xsl:attribute name="select">
      <xsl:for-each select="$exprs">
         <xsl:value-of select="." />
         <xsl:if test="position() != last()"> | </xsl:if>
      </xsl:for-each>
   </xsl:attribute>
</oxsl:variable>

You should be able to create the rest of the stylesheet very straight-forwardly.

13.

Selecting only one node value out of the many identical values

Jeni Tennison


> this XML is genereted based on query from a user and comes from a
> database. I want to display to the user only the available "brands"
> of shirts Ie. output shall be only "Arrow" and "Lee" in my case. and
> the user can then further query on the subitems like "size" and
> "color". SO I want a  Select "brand" from "clothes/category/sno"
> where "there is no repetiion"
	

Getting the 'distinct' values involves getting only those values that haven't already occurred in the document. The basic method of doing this is to find all the nodes where there isn't a preceding node that has the same value in the document.

In your case you want all the sno elements:

  clothes/category/sno

And then you want to filter in those where there aren't any preceding (sibling) sno elements whose brand is the same as the brand of the particular sno element:

  clothes/category/sno[not(preceding-sibling::sno/brand = brand)]

If you have *lots* of sno elements, then another possibility is to create a key that indexes the sno elements by brand:

<xsl:key name="sno-by-brand" match="sno" use="brand" />

You can then find all the sno elements with a particular brand (say 'Arrow') with the key() function:

  key('sno-by-brand', 'Arrow')

and you can find all the sno elements that are first in the list of sno elements of a particular brand, as returned by the key, with:

  clothes/category/sno[generate-id() =
                       generate-id(key('sno-by-brand', brand)[1])]

or:

  clothes/category/sno[count(.|key('sno-by-brand', brand)[1]) = 1]

14.

Selecting all but two elements by name

Jeni Tennison

It depends how much you care about namespaces.

    *[not(name()='a' or name()='b')]

Gives you all the elements aside from a and b elements that are in the default namespace in their document. I'd tend to steer clear of this because the default namespace could be anything at all.

    [not(self::a or self::b)]

Gives you all the elements aside from a and b elements that are in no namespace. Use this if the a and b elements that you want to ignore don't have a namespace or if you're not using namespaces at all.

    *[not(local-name()='a' or local-name()='b')]

Gives you all the elements aside from a and b elements in any namespace. Use this if you want to ignore all a and b elements no matter what namespace they're in.

15.

Get the second para after the heading

Jeni Tennison


 > I'm trying to find a way of saying 'output the third paragraph after
 > the heading containing the text "foo"' -- to no avail.

It's probably easiest to do this by only selecting that paragraph rather than (as you are doing currently, it seems) trying to match only that paragraph.

You can get the h1 whose value is 'Best Section' with:

   h1[. = 'Best Section']

and then get the second following sibling p element from that with:

   h1[. = 'Best Section']/following-sibling::p[2]

If you only apply templates to that p element, then that's the only output that you'll get.

16.

Select all children except ...

Joerg Pietschmann



> I don't really know if "not(self::c)" is more efficient than "name()!='c'"
> but I prefer it for some reason, maybe because there's no function call?

I don't think there is a performance difference for most XSLT processors.

However, using self:: is more robust when it comes to elements in a namespace. If you write


 name()='foo:bar'

only elements with the literal name foo:bar fulfill this condition but not baz:bar, even if the baz prefix is bound to the same namespace. Like in

 <sample xmlns:foo="uri:sample:stuff" xmlns:baz="uri:sample:stuff">
   <foo:bar/>
   <baz:bar/>
 </sample>

the inner elements have different a name(), but they are both matched by self::foo:bar

17.

Select an element based on childrens name

Wendell Piez


>>   if i want to select all elements that have at least one, say,
>>"book" child element, it seems i can do it this way:
>>   //*[name(child::*) = "book"]          long way
>>   //*[name(*) = "book"]                 shorter way
>>1) is this the easiest way to do it?
>
>//*[book]
>
>>2) why doesn't the following work as well?
>>   //*[name(node()) = "book"]
>
>i'm not sure, but i think this will compare the name of the first child only.

Yup. The name() function takes a node set as its argument, and returns the name of the first node in the node set.

All three of the name tests will fall into this category.

/*[book] is the way to do it, since the XPath expression "book" (short for child::book) returns true if there are any book child elements (not just if the first element child is a book)

//*[book] is short for

/descendant-or-self::node()/child::*[child::book]

(Traversing from the root, get all nodes at any depth, then get any element child that has a book element child.)

name(node())='book' might be seeming to fail where name(*)='book' seems to work due to whitespace in the input. If the first element child is a <book> but it is preceded by a text node (even one that just contains a line feed), the test name(node())='book' on its parent will test false whereas name(*)='book' tests true.

I hope this clarifies!

18.

Selecting elements in a given namespace

Jeni Tennison



> if i want to select all nodes in a given namespace ("auth"), it
> appears i can use:
>
>   //namespace::auth

That will select all the *namespace*nodes* named "auth". Namespace nodes are nodes that represent a prefix-namespace binding. Such a namespace node will exist on every element within the scope of the xmlns:auth="..." namespace declaration.

If you want to select all the elements in the namespace associated with the prefix 'auth', use:

  //auth:*

All attributes in that namespace with:

  //@auth:*

Note that the 'auth' namespace must be declared in the stylesheet (or in whatever context you're selecting the nodes). A stylesheet will not automatically pick up on the fact that the namespace is declared in the XML document.


> so what is the XPath expression to match, say, the <last-name>
> elements within this namespace?

XPath expressions don't match elements, they select them. If you want to select the last-name element in the 'auth' namespace, use:

  //auth:last-name

or, better:

  /auth:author/auth:last-name

To *match* the auth:last-name element with an XSLT pattern, you can use:

  auth:last-name

Note that in your example, the <last-name> element is not in the namespace associated with the 'auth' prefix since it does not have a prefix and there is no default namespace declaration. In your example, only the <auth:author> element is in the 'auth' namespace.

19.

Selective processing of first n elements

Passin, Tom



> I can't seem to get something 
> which will only 
> print/process the first 'x' (in my case x is 2) elements and 
> then bail out 
> of the for-each or just simply ignore the remaining ones found.

This is a good chance for you to start getting into XSLT-think mode. Instead of limiting a loop, think of selecting just the elements you want. The simplest example is to put this into a template -

   <xsl:apply-templates select='element-to-work-with[3 > position()]'/>

Here you select just the first two nodes, and so whatever template processes them only ever sees those two nodes to work on.

The most common and fundamental thing one usually does in xslt is to select a set of nodes to work with. Therefore, think carefully about how to select exactly the nodes you want.

[Note that the sense of the test is reversed to avoid having to write &lt;, which will work fine but is harder to read]

20.

Pick first few elements as file sample

Dimitre Novatchev


> Need a simple transformation to reduce the size of a file... so like I
> just need to see the first 10 elements of an xml source which is 10
> megs!  Those first 10 elements would be the first 10 child elements to
> the source including their child elements.  Here's what ISN'T working:


>    <xsl:template match="node | @*">
>        <xsl:apply-templates select="node[position() &lt;= 10] | @*" />
>    </xsl:template>

Here's what is working:

<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 
 <xsl:strip-space elements="*"/>
 
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="/*/*[position() > 10]"/>
  
<xsl:template match="*[ancestor::*[3]]"/>

</xsl:stylesheet>

21.

Select one from a number of items

Michael Kay



> I have a gallery.xml file containing a number of images:
>
> <gallery>
>     <image>
>       <filename>1.jpg</filename>
>     </image>
>     <image>
>       <filename>2.jpg</filename>
>     </image>
>     <!-- etc -->
>     <image default="true">
>       <!-- optional attribute applies to this element -->
>       <filename>10.jpg</filename>
>     </image>
> </gallery>
>
> and my xslt has to do the following thing -- look for an
> image marked with a "default" attribute, and if it's not
> found, use the first image instead. So I'm looking for the
> position number of the marked element, and if I can't find
> it, using 1:

Try

<xsl:copy-of select="(image[1] | image[@default='true'])[last()]"/>

22.

Namespace nodes.

Michael Kay





> Question regarding "<xsl:apply-templates 
> select="*"/>". 
> Is this going to be applied for each node and attribute > also? 
> So for eg:
>  if I have <ABC xmnls:ns2="http://"> would "<template 
>  match=*>" be called for ABC and then xmnls:ns2 also.

1. select="*" only selects child elements, it does does not select attributes, namespaces, text nodes, comments, or processing instructions.

2. xmlns:ns2 in the XPath model is not an attribute. It turns into a namespace node. You cannot apply-templates to namespace nodes (well, you can, but no template rule will ever match them).

To get rid of namespace nodes that are being copied from the source document, in XSLT 2.0 you can use <xsl:copy copy-namespaces="no">. In 1.0 you have to use <xsl:element name="{name()}" namespace="{namespace-uri()}">.