Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT - count the number of child elements by using XPath

I have the following XML file that stores movies and actors details:

<database
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="movies.xsd">

<movies>
    <movie movieID="1">
        <title>Movie 1</title>
        <actors>
            <actor actorID="1">
                <name>Bob</name>
                <age>32</age>
                <height>182 cm</height>
            </actor>
            <actor actorID="2">
                <name>Mike</name>
            </actor>
        </actors>
    </movie>
</movies>

</database>

If the actor element contains more than one child elements (in this case it's name, age and height) then I want to display its name as a hyperlink.

If, however, the actor element only contains one child element (name), then it should be displayed as plain text.

XSLT:

<xsl:template match="/">
    <div>
      <xsl:apply-templates select="database/movies/movie"/>
    </div>
  </xsl:template>

  <xsl:template match="movie">
    <xsl:value-of select="concat('Title: ', title)"/>
    <br />
    <xsl:text>Actors: </xsl:text>
    <xsl:apply-templates select="actors/actor" />
    <br />
  </xsl:template>    

<xsl:template match="actor">
    <xsl:choose>
        <xsl:when test="actor[count(*) &gt; 1]/name">
            <a href="actor_details.php?actorID={@actorID}">
                <xsl:value-of select="name"/>
            </a>
            <br/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="name"/>
            <br/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

So in this case Bob should be displayed as a hyperlink, and Mike should be displayed as plain text. However, my page displays both Bob and Mike as plain text.

like image 696
Alex Avatar asked Apr 15 '13 18:04

Alex


People also ask

Does XSLT use XPath?

XSLT uses XPath to find information in an XML document. XPath is used to navigate through elements and attributes in XML documents. In the transformation process, XSLT uses XPath to define parts of the source document that should match one or more predefined templates.

How do you set a counter in XSLT?

XSLT is a functional language, not a procedural language, so you can't declare a counter. You can use xsl:number to get the position of the current node in its parent, if that helps. You can coerce a string to a number by using the XPath number() function.


1 Answers

Your first xsl:when test is incorrect here

<xsl:when test="actor[count(*) &gt; 1]/name">

You are already positioned on an actor element here, so this will be looking for an actor element that is a child of the current actor element, and finding nothing.

You probably just want to do this

<xsl:when test="count(*) &gt; 1">

Alternatively, you could do this

<xsl:when test="*[2]">

i.e, is there an element in the second position (that saves counting all elements, when you only really want to check there is more than one),

Or perhaps you want to check the current actor element has an element other than name?

<xsl:when test="*[not(self::name)]">

As an aside, it might be better to put the test in a template match, rather than use an xsl:choose.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes" encoding="utf-8" omit-xml-declaration="no"/>

   <xsl:template match="/">
      <div>
         <xsl:apply-templates select="database/movies/movie"/>
      </div>
   </xsl:template>

   <xsl:template match="movie">
      <xsl:value-of select="concat('Title: ', title)"/>
      <br/>
      <xsl:text>Actors: </xsl:text>
      <xsl:apply-templates select="actors/actor"/>
      <br/>
   </xsl:template>

   <xsl:template match="actor">
      <xsl:value-of select="name"/>
      <br/>
   </xsl:template>

   <xsl:template match="actor[*[2]]">
      <a href="actor_details.php?actorID={@actorID}">
         <xsl:value-of select="name"/>
      </a>
      <br/>
   </xsl:template>
</xsl:stylesheet>

Note that the XSLT processor should match the more specific template first in this instance.

like image 99
Tim C Avatar answered Oct 15 '22 09:10

Tim C