Re: [xsl] numbering output and other newbie issues

Subject: Re: [xsl] numbering output and other newbie issues
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Fri, 24 Feb 2006 15:33:31 -0500
Dear Terry,

Welcome to XSLT.

In order to handle your questions, it's actually helpful to go backwards.

The reason you are getting only "you" as output when you use the instruction xsl:value-of rather than xsl:copy-of is that value-of is defined to return a string value. Since you are selecting a set of nodes (the Answer elements selected by the XPath "Story/GrammarQuiz/Item/Answer", traversing from the root), the processor has a set of strings to deal with, not a single string. The rule is, when converting a set of nodes to a string, take the string value of the first node in the set, in document order. In this document, the Answer element that appears first has the value "you".

Now, to your numbering. The reason your instruction <xsl:number count="*"/> isn't working is that this instruction works relative to the "context node" from which it is called, namely the node that has been matched by the template in which it appears. Your template matches "/", the root node of the document. But your instruction says to count="*", meaning any element. The root of the document is not an element, so no number appears.

In order to make this work, you need to invoke xsl:number from an appropriate context. In your case, since you want to number the Answers, the context node you need will be each of the Answer elements in turn. To get them to be the context, you need a new template, like this:

<xsl:template match="Answer">
  <xsl:number level="any" format="1. "/>

The reason you need level="any" is that you want to number all the Answers in your document together. The default would be to number each within the context of its parent element (think items in a list), which in your case would produce a series of Answers numbered "1. " (no good).

Now, how to get this template to operate? The processor works basically from the top, matching the root first and then proceeding from there. If it is told to apply a template to a node but doesn't have a template to match it, it has default templates it uses instead. (This is a critical piece of information that many newcomers miss, so watch out.) In your case, you do have a template to match the root, so let's look at that:

<xsl:template match="/">
  <xsl:copy-of select="Story/GrammarQuiz/Item/Answer" />

This is fine except it says to copy the Answer elements, which it's doing, but you need to do more; you need to apply your other template to them, like this:

<xsl:template match="/">
  <xsl:appy-templates select="Story/GrammarQuiz/Item/Answer"/>

Combine this with the template I showed you above and you should get what you want (in two templates, seven lines: not bad).

If you're wondering how this results in getting your Answers out, the reason is that when the template matching "Answer" says to apply templates, it'll go down to the contents of each of the Answers, namely (in this document) the text nodes that appear inside them. These text nodes are matched by one of the built-in templates I mentioned above, which copies their values out, and voila.

Try it and see. Ask again with any questions. Read up on the XSLT processing model, particularly on templates, the apply-templates instruction, and the built-in default templates. You've concocted a good answer to start because it's simple, but to do it properly you do have to use these template things, which is the best way to get started with the language, as they're the secret to just about everything.


At 03:19 PM 2/24/2006, you wrote:
I am a newbie to xsl stylesheets as well as xml. I have set myself a specific task as my first "assignment." I have a set of English grammar exercises for which I want to build an answer key. The exercises are not in XML format yet, so I have control over the XML format as well as the XSL stylesheet.

Here is my first trial XML document.

<?xml version="1.0" encoding="UTF-8"?>
<chapter>The Sentence Base?</chapter>
<GrammarTopic>Finding Subjects</GrammarTopic>
<Directions>Underline the subject in each sentence. If the subject is an understood you, write you.</Directions>
<Item>1. Find your seats quickly. <Answer>you</Answer></Item>
<Item>2. During open auditions there are very few empty <Answer>seats</Answer> in the auditorium.</Item>
<Item>3. Have <Answer>you</Answer> heard the director's name?</Item>

And here is the stylesheet.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl=""; version="1.0">

<xsl:template match="/">
<xsl:copy-of select="Story/GrammarQuiz/Item/Answer" />


The result is:

<?xml version="1.0" encoding="utf-8"?><Answer>you</Answer><Answer>seats</Answer><Answer>you</Answer><Answer>actress</Answer><Answer>director</Answer>

This result is okay, but I would like to number each answer. I have tried to add this line after the copy-of select line:

<xsl:number count="*" format="1. "/>

This line does not change the output.

There are other anomalies (or what appear to me to be anomalies.) For example, when I change copy-of select to value-of select I get this output:

<?xml version="1.0" encoding="utf-8"?>you.

I am using SAXON 6.5.4 on a Windows laptop. I have tried the same on Mas OS X with XSLT Tester with the same result-- which only proves that it is my stylesheet that is at fault (along with my thinking).

Any help would be appreciated.

Wendell Piez                            mailto:wapiez@xxxxxxxxxxxxxxxx
Mulberry Technologies, Inc.      
17 West Jefferson Street                    Direct Phone: 301/315-9635
Suite 207                                          Phone: 301/315-9631
Rockville, MD  20850                                 Fax: 301/315-8285
  Mulberry Technologies: A Consultancy Specializing in SGML and XML

Current Thread