Re: [xsl] Applying an XSLT-file to specific nodes within an XML file

Subject: Re: [xsl] Applying an XSLT-file to specific nodes within an XML file
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 16 Jul 2009 17:27:55 -0400
Mark,

You write:
A possible solution to this would be to write a simple XQuery script to extract the relevant <msDesc> nodes and put them in a separate XML file, which can then be transformed. That requires a lot of manual labour (especially since my titles go from AAAA to PPPP...), so I was wondering whether there is a work-around or short-cut within XSL?

The part of XQuery that would do what you want is XPath, and XPath is also available in XSLT: in fact it's close to the heart of it. What you want to do is what XSLT was designed to do, so you should use it.


For example, if your XSLT currently has something like:

<xsl:template match="/">
  <html>
    <head>... stuff ... </head>
    <body>
      <xsl:apply-templates/>
    </body>
  </html>
</xsl:template>

you could amend this simply by saying

<xsl:apply-templates select="//msDesc[*/title='AAAA']"/>

instead of the apply-templates you already have, and the selection you want would occur.

Or, if your XSLT has something like this:

<xsl:template match="mss">
  <div class="mss">
    <xsl:apply-templates/>
  </div>
<xsl:template>

you could change that xsl:apply-templates like this:

<xsl:apply-templates select="msDesc[*/title='AAAA']"/>

and do it that way.

(There is a subtle but important distinction between the two XPath expressions I just used.)

The next question you should be asking is why this works. The answer to that lies in XSLT's traversal of the source document while it builds the result. By selecting nodes in the source document to apply templates to, instead of simply applying templates to the children in the normal manner (which is what happens by default, to allow a complete tree traversal), you can effectively direct exactly which nodes in your input are processed where.

That's what we call a "pull" approach since it works by actively selecting nodes from the source. An alternative method, and one that is often preferable, is to use what we call "push". This technique allows the default traversal to occur, except it intervenes at points to affect processing by matching nodes selectively (as opposed to selecting them to apply templates to).

In your case, you might get what you want by adding something as simple as this to your stylesheet:

<xsl:template match="msDesc[not(*/title='AAAA')]" priority="2"/>

What does this do? It matches msDesc elements that have no 'title' grandchild with the value 'AAAA' and -- does nothing with them. So they do not appear in the result document (they are, effectively, dropped, leaving only the ones you want).

You could even leave the 'priority="2"' off this template if you have confidence that it will override any other templates matching "msDesc" (which it probably does, depending on a couple of rules you will also need to learn in order to master the language).

This is all really easy -- if you know how it works. If you're guessing it can be baffling. (It's fortunate this list is around.)

Cheers,
Wendell

At 04:22 PM 7/16/2009, you wrote:
Hi all,

Hopefully someone can assist me in solving a (probably rather basic) problem related to restricting the execution of an XSLT file to a specific part of an XML file.

My XML file looks like this (very simplified):

<mss>
      <msDesc>
              <msItem>
                      <title>AAAA</title>
              </msItem>
      </msDesc>
      <msDesc>
              <msItem>
                      <title>BBBB</title>
              </msItem>
      </msDesc>
      <msDesc>
              <msItem>
                      <title>CCCC</title>
              </msItem>
      </msDesc>
</mss>

I have written an XSLT file which transforms my entire XML into a nice HTML file. I would now like to restrict the XSLT file so that it ONLY transforms the <msDesc> nodes which have a <title> tag with the value 'AAAA'. So, in simpler terms, I would like to say to my XSL file:

1. Find all <msDesc> nodes which have a <title> tag with the value 'AAAA'.
2. Apply the following <xsl:template> transformations, but only to the <msDesc> nodes specified above.
3. [And then all my <xsl:template> transformations]


A possible solution to this would be to write a simple XQuery script to extract the relevant <msDesc> nodes and put them in a separate XML file, which can then be transformed. That requires a lot of manual labour (especially since my titles go from AAAA to PPPP...), so I was wondering whether there is a work-around or short-cut within XSL?

I hope this makes sense. I'm sure there's something obvious I'm missing...

Many thanks in advance,
Mark


======================================================================
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