[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] Matching or selecting template problem


Subject: Re: [xsl] Matching or selecting template problem
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 15 Jan 2009 11:25:37 -0500

Charles,

At 10:34 AM 1/15/2009, you wrote:
There are some advanced aspects of XSLT that I just don't seem to "get."

They'll click. Persistence is key.


(And don't worry about what I think. I assume all my former students are brilliant -- they were smart enough to come listen to me! :-P But learning anything takes practice, and Aha moments come at different times for different people.)

As for the essence of the problem here, you've already put your finger on it. Selecting and matching do not have to be symmetrical. Indeed, they're frequently not.

It's perfectly fine and useful to do:

<xsl:template match="sec">
  <xsl:apply-templates select="title"/>
</xsl:template>

<xsl:template match="sec/sec/title">
  ... do something ...
</xsl:template>

Notice that the selection grabs the title from among the children of the sec (one level down), but the match on the title looks up the tree two levels before it succeeds in matching.

An experienced XSLTer will look at this code and be able to say that assuming there are no other templates getting in the way, the second template will fire when the title is selected in the first template *if and only if the sec matched by the first template is also directly inside a sec*. This is because we can see that the title selected in the first template has to match "sec/title", so the only question is what's the sec inside of?

By the same token, if the first template had matched "sec/sec", that particular question would be answered, and we could be certain that the second template does match the title selected in the first one.

Generalizing them: you can select elements from wherever you like in the tree using whatever paths you like, and then match them using expressions that happen to match them.

As for the problems you're having matching, what you're running into is a set of restrictions on the grammar for XPath Patterns, which is not the same as path expressions (although it's defined to look like a subset). The simple version is that patterns allow only looking "down" the tree, using the child:: axis or the "//" operator. So "sec//title" is a legal pattern, and can be used for matching, but "sec/title/following-sibling::*" is not.

This doesn't prevent you from *selecting* the following siblings of the title from inside a template matching "title", using the path expression "following-sibling::*" to select them, and then *matching* them using whatever patterns you want, such as "sec/*", or "ul" in mode "title-siblings", or whatever.

Behind all this is a critical point I think David made: this isn't sequential text processing, and there are no tags involved. This is tree processing. Your data, for example, looks like this to me:

concept
  title
  prolog
  conbody
    section
      title
    section
      title
    section
      title
    p
    p
    ul
      li
        p
      li
        p
          xref

etc. This means I don't see the 'p' element starting after the 'section' element ends (no start- or end-tags here). I see it inside the conbody element, appearing after the sections. And ordinarily I don't think of processing it after the section or the section title; I think of processing it inside the conbody, same as I do the sections.

Cutting out the extraneous stuff, what this means is that you want to change this:

conbody
  sections
  *loose stuff*

into this:

conbody
  sections
  section
    *loose stuff*

And you can do this entirely in the template for conbody:

<xsl:template match="conbody">
  <xsl:copy>
    <xsl:apply-templates select="section"/>
    <section>
       <xsl:apply-templates select="[... loose stuff ...]"/>
    </section>
  </xsl:copy>
</xsl:template>

Now a bit of confusion, since the question arises how to fill in "loose stuff".

I think it was Ken who offered a simple way to do that before:

<xsl:template match="conbody">
  <xsl:copy>
    <xsl:apply-templates select="section"/>
    <section>
       <xsl:apply-templates select="*[not(self::section)]"/>
    </section>
  </xsl:copy>
</xsl:template>

... which is good, unless you think that the fancy XPath "*[not(self::section)]" will confuse the maintenance programmer.

Another way to do it:

<xsl:template match="conbody">
  <xsl:copy>
    <xsl:for-each select="section">
      <section>
        <xsl:apply-templates/>
      </section>
    </xsl:for-each>
    <section>
      <xsl:apply-templates/>
    </section>
  </xsl:copy>
</xsl:template>

and then

<xsl:template match="section"/>

This is also elegant, and it works -- with arguably the opposite problem, since it's very subtle, since what it does is (1) create a section for every section, and then (2) make a section, and inside it (3) process everything, but this time throw the sections away.

A third way to do it would use a special mode on one side or the other, instead of the for-each instruction.

Note that in any case you may also want conditional testing to make sure you actually have loose stuff, so as not to create an extra section when you don't want one.

You may also run into trouble if your loose stuff is scattered throughout your conbody, not just after the last section, and you want to group it into sections. Note my use of the term "group", since that is, in effect, a grouping problem.

I hope this helps.

Cheers,
Wendell



======================================================================
Wendell Piez                            mailto:wapiez@xxxxxxxxxxxxxxxx
Mulberry Technologies, Inc.                http://www.mulberrytech.com
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
Keywords