xsl-list
[Top] [All Lists]

Re: Answers to review questions in "Beginning XSLT": Chapter 5

2003-03-12 06:08:53
Hi Lars,

For now I'm omitting the questions themselves, since I don't yet
have explicit permission to copy them. (Jeni, would that be OK?)

Yes, I'm sure that would be fine. It would also mean that people who
don't have the book are able to understand what you're answering.

1. No nodes match the select pattern, so no templates get applied,
   so there is no output for that apply-templates.

That's correct, though it would be more accurate to say that "no nodes
get selected by the select expression". Remember that the select
attribute (on <xsl:apply-templates>, and elsewhere) is for *selecting*
things with an *expression* and the match attribute (on <xsl:template>
and elsewhere) is for *matching* things with a *pattern*.

2. Use one template with match="Description//Film", and another with
   match="Channel/Film".  Or use match="Film" for one of them, and
   the more specific match for the other.

That's right.

3. This code produces a <div> element with contents as follows:
   if there are Film children of the current node, they are processed
   with xsl:apply-templates; otherwise, the message "No films..."
   is included.

Right.

4. Rewriting of preceding code using <xsl:choose> instead of <xsl:if>:

  <div>
    <xsl:choose>
         <xsl:when test="Film">
        <xsl:apply-templates select="Film" />
      </xsl:when>
      <xsl:otherwise>
        No films showing on this channel.
      </xsl:otherwise>
    </xsl:choose>
  </div>

    The advantage of the latter form is that if there are no Film
    children of the current node, <xsl:apply-templates> doesn't get
    evaluated.  Also, it may be a little more clear to the human reader
    that there are two mutually exclusive possibilities for the <div>
    elements contents: the results of applying templates to "Film" children,
    and the "No films..." message.

    The advantage of the <xsl:if> form is that it is shorter, and for
    that reason perhaps easier to read.

Yep. Another advantage with the <xsl:choose> form is that it makes it
easier to add an element around the results of processing the <Film>
elements:

  <div>
    <xsl:choose>
      <xsl:when test="Film">
        <table>
          <xsl:apply-templates select="Film" />
        </table>
      </xsl:when>
      <xsl:otherwise>
        No films showing on this channel.
      </xsl:otherwise>
    </xsl:choose>
  </div>

5. The given code inserts a 20x20 icon iff there is a flag attribute
   on the current node.  There is a specific icon for when the value
   of the flag attribute is 'favorite', and another for when it is
   'interesting', and a third for any other value.  If the flag attribute
   doesn't exist, no icon is inserted.

Right.

6. Rewriting of the above "as a sequence of <xsl:if> elements" instead of
   using <xsl:choose>:  (One could take "sequence" to mean "no nesting",
   but that would hobble the <xsl:if> and I don't think that was the
   intent of the question, so I'm going to nest them.)

    <xsl:if test="@flag">
      <xsl:if test="@flag = 'favorite'">
        <img src="favorite.gif" alt="[Favorite]" width="20" height="20" />
      </xsl:if>
      <xsl:if test="@flag = 'interesting'">
        <img src="interest.gif" alt="[Interest]" width="20" height="20" />
      </xsl:if>
      <xsl:if test="@flag != 'favorite' and @flag != 'interesting'">
        <img src="flag.gif" alt="[Flagged]" width="20" height="20" />
      </xsl:if>
    </xsl:if>

The wrapping <xsl:if> is actually superfluous. @flag = 'favorite' and
@flag = 'interesting' obviously cannot be true if there is not flag
attribute, but it is also the case that @flag != 'favorite' cannot be
true if there is no flag attribute. Perhaps you included the <xsl:if>
so that all the instructions are ignored if the flag attribute doesn't
exist.

7. test="Film/@year > 1950"

Right.

8. Film[starts-with(Title, 'Romeo')]

Right.

9. test="number()"
   This converts the current node's value to a number, which is then
   converted to true if non-zero, false if zero or NaN.

Right.

10. To test whether the current node's value is a number,
   use test="number() or number() = 0" which gives the desired result.
   Is there a simpler way?

   I tried test="number() != NaN" but this was always false (Why?  Is
     NaN not recognized as the numeric constant?  No, I just learned that
     it's because NaN is not equal to itself!  See the FAQ,
     http://www.dpawson.co.uk/xsl/sect2/N5846.html);

   test="number() != 'NaN'" was always true;

   test="number() != number('NaN')" also was always true (see above,
     NaN is not equal to itself);

   test="string(number()) != 'NaN'" worked but this is not necessarily
    simpler.  (Btw I see from the FAQ that this is a standard way
    to check whether a node's value is a number.  There Jeni Tennison
    also suggests test="@value &lt;= 0 or @value > 0" or
    test="number(@value) = number(@value)".  However, these both rely
    on properties of NaN that are not covered in ch. 5.)

   Question: is there a way to represent the numeric constant NaN
   in an expression?  Apparently the unquoted NaN is not recognized
   as such; I guess it's interpreted as "the value of the child element
   named NaN."

You've answered a lot of this yourself in your follow-up mail. I don't
think that there's a right answer to this question: it's more a prompt
to get you to explore the properties of NaN, number(), boolean(),
string() and comparison operators. Personally, I usually use:

  string(number()) != 'NaN'

because it makes it clear what I'm actually testing: number() =
number() is a bit esoteric, even if it is neater.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


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