There are Monsters in My Closet
or
How Not to Use XSLT
R. Alexander Milowski
milowski@sims.berkeley.edu
School of Information Management and Systems
#1
What's Wrong?
Overuse of:
xsl:if
xsl:choose
xsl:for-each
xsl:value-of
None of the assignments really required these.
There are certainly better ways.
#2
Er... But I like if, choose, for-each...
Yes, they are simple.
But you are duplicating what the processor will do by default.
You're excluding many optimization opportunities!
You're disabling extensibility!
So, we're going to run through some examples,... good, bad, and indifferent.
#3
Pull vs. Push
Pull Model:
<xsl:template match="people"> <top> <name><xsl:value-of select="person/last-name"/></name> </top> </xsl:template>
Push Model:
<xsl:template match="people"> <top> <xsl:apply-templates/> </top> </xsl:template> <xsl:template match="last-name"> <name><xsl:apply-templates/></name> </xsl:template>
Example Content: people-1.xml people-2.xml
Example Transformations: pull-person.xsl push-person.xsl
#4
Make the Built-in Rules do the Work!
Rule: Use the built-in rules when you can!
There are two built-in template rules:
This traverses your descendants:
<xsl:template match="*|/"> <xsl:apply-templates/> </xsl:template>
This copies text to the output:
<xsl:template match="text()|@*"> <xsl:value-of select="."/> </xsl:template>
Note: Attribute text is only copied if you apply templates to an attribute.
These get dropped from the output:
<xsl:template match="processing-instruction()|comment()"/>
You get these for free and the processor will do them in an "optimal" way.
#5
xsl:value-of
If you have:
<name>Alex</name>
You shouldn't do this:
<xsl:template match="name"> <name><xsl:value-of select="."/></name> </xsl:template>
But rather, do this:
<xsl:template match="name"> <name><xsl:apply-templates/></name> </xsl:template>
Rule: Only use xsl:value-of when you absolutely have to do so.
#6
xsl:value-of and Descendants
If you have:
<name>Bingo<trade-mark>Palace</trade-mark></name>
This won't do what you want:
<xsl:template match="name"> <p><xsl:value-of select="."/></p> </xsl:template>
You'll get this:
<p>BingoPalace</p>
instead of:
<p>Bingo Palace</p>
#7
Use the Context
Rule: Don't replicate the context.
When a template is applied, the context is changed to the matching node.
So use it!!!
Replicating the context is just extra work.
Keep in mind every select expression causes a document traversal of some sort.
Minimizing document traversals optimizes your stylesheet for performance.
Rule: Don't use rooted XPath expressions unless you absolutely need it.
#8
Use the Context - Example
Suppose we have:
<syllabus> <name>XML and Related Technologies</name> <office-hours>103B South Hall at Monday 2-3:30pm and Wednesday 2-3:30pm</office-hours> <short-description>...</short-description> <schedule>...</schedule> </syllabus>
This is extra work:
<xsl:template match="syllabus"> <div> <p>Course Summary:</p> <xsl:apply-templates select="/syllabus/name"/> <xsl:apply-templates select="/syllabus/short-description"/> </div> </xsl:template>
This is much better and more flexible:
<xsl:template match="syllabus"> <div> <p>Course Summary:</p> <xsl:apply-templates select="name|short-description"/> </div> </xsl:template>
#9
Use Document Order
Rule: Always start by using document order.
Unless you need to re-order elements, use the document order.
When expressions evaluate, they are return in document order:
<xsl:apply-templates select="A|B"/>
at this context:
<A/><C/><B/><C/><C/><A/>
returns:
<A/><B/><A/>
#10
Ranking Elements by Position
Rule: Selecting exact positions of children is fragile and so avoid doing it.
You don't need to call out a child's position.
Use document order so that instead of this:
<xsl:template match="description"> <div> <xsl:apply-templates select="p[1]"/> <xsl:apply-templates select="p[2]"/> <xsl:apply-templates select="p[3]"/> </div> </xsl:template>
do this:
<xsl:template match="description"> <div> <xsl:apply-templates select="p"/> </div> </xsl:template>
Example: doc-order.xml no-doc-order.xsl doc-order.xsl
#11
xsl:for-each vs. templates
Rule: Iterating expressions with xsl:for-each is generally bad style.
You can typically do every xsl:for-each with an xsl:apply-templates action.
But you might need modes...
And maybe named templates...
These will are explained in the adjoining slides.
#12
Re-casting xsl:for-each as a Template
Suppose I want to output a 'p' element for each recommened text.
You might do the following:
<xsl:template match="syllabus"> <xsl:for-each select="recommened-books/book"> <p><xsl:apply-templates/></p> </xsl:for-each> </xsl:template>
But what if I add 'pamphlet' or 'article' elements to the children (i.e. siblings of 'book')?
The above statement is broken if I want all recommended readings.
I'd have to change that template.
#13
Templates afford Extensibility
In the previous example, I could instead have:
<xsl:template match="syllabus"> <xsl:apply-templates select="recommended-books"/> </xsl:template> <xsl:template match="book"> <p><xsl:apply-templates/></p> </xsl:template>
When I add 'pamphlet' or 'article' elements to the children (i.e. siblings of 'book') I just add:
<xsl:template match="pamphlet"> <p><xsl:apply-templates/></p> </xsl:template> <xsl:template match="article"> <p><xsl:apply-templates/></p> </xsl:template>
But it gets even better...
#14
Importing Stylesheets for Extensiblity
I can leave my old stylesheet alone and do the following:
<xsl:import href="syllabus.xsl"/> <xsl:template match="pamphlet"> <p><xsl:apply-templates/></p> </xsl:template> <xsl:template match="article"> <p><xsl:apply-templates/></p> </xsl:template>
This will merge two stylesheet rules so that the importing styleesheet's rules have precedence over the other.
Transformation example: recommended-base.xsl recommended.xsl
Input documents: books-1.xml books-2.xml
#15
Avoid // in Expressions
Rule: Avoid using // to search your descendants.
The "//" expression will cause you to visit every descendant.
That means that "//something" is about the most expensive operation.
You are writing your stylesheet for a particular kind of document.
Use that structure in your stylesheet.
#16
Contracts, Schemas, and Stylesheets
Transformations assume some kind of structure:
It is a kind of "contract" between the stylesheet and the input.
Typically, DTDs or XML Schemas encode this contract.
Sometimes there are additional application requirements.
Validating content will help ensure your stylesheet works properly.
Comment your assumptions in your stylesheet.
#17
Use of xsl:element and Readability
There is no difference between xsl:element
<xsl:element name="e:Body">...</xsl:element>
and a literal element
<e:Body>...</e:Body>
Rule: If you don't have "dynamic" names (e.g. parameters or expressions), use a literal result element.
Literal result elements are the norm and xsl:element is the exception.