Re: [xsl] Test for child node categories

Subject: Re: [xsl] Test for child node categories
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 05 Jul 2007 11:43:09 -0400
Trevor,

David has suggested how you can use brute-force XPath for this sort of test. In XSLT 1.0, keys can also make things just a touch easier:

<xsl:key name="textcontent-by-id" use="generate-id()"
  match="text()[normalize-space()] | em | sub | sup"/>

(Only text nodes that have non-whitespace content are returned by this key, to avoid problems with cosmetic whitespace. If you have clean whitespace stripping you can remove the normalize-space() predicate.)

<xsl:key name="blockcontent-by-id" use="generate-id()"
  match="para | note | warning | table | img | define | list | ul | ol"/>

Then in an appropriate context:

<xsl:template match="answer">
  <xsl:choose>
    <xsl:when test="node()[key('textcontent-by-id',generate-id()] and
                    node()[key('blockcontent-by-id',generate-id()]">
     ... both textcontent and blockcontent children exist ...
    </xsl:when>
    <xsl:when test="node()[key('textcontent-by-id',generate-id()]">
     ... textcontent children exist ...
    </xsl:when>
    <xsl:when test="node()[key('blockcontent-by-id',generate-id()]">
     ... blockcontent children exist ...
    </xsl:when>
    <xsl:otherwise>
     ... any children are in neither set
         (if the XML is valid there are no children) ...
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

This works because a node will be returned by the key only if it matches the pattern in the key declaration. As you recall, a node-set with members in it tests "true" and one with none tests "false", so the expression "key('blockcontent-by-id',generate-id()" returns true only if the node that provides the ID (the context node for the expression) is one of the node types matched in the declaration.

It's probably no clearer to the neophyte than brute force, and may be a tiny bit slower, but it is a bit easier to maintain, because it encapsulates the logic of your content models somewhat in your key declarations.

The other part of your problem, the wrapping of consecutive non-blockcontent child nodes in divs, is actually rather harder in XSLT 1.0, though not impossible. (But it's fairly straightforward in 2.0, which you should know.) Indeed, a solution for this problem can also make the problem you've just asked about go away ... instead of distinguishing between three types of parents (ones containing blocks, ones containing text content, and ones with a mix), you just pick up all the children and process them with the necessary grouping logic.

Cheers,
Wendell

At 09:02 AM 7/4/2007, you wrote:
I have the following element definition (I've cut it down slightly for
reasons of space but the principle is clear):

  <!ENTITY % simple "#PCDATA">
  <!ENTITY % markup "em|sub|sup">
  <!ENTITY % textcontent "%simple;|%markup;">
  <!ENTITY % lists "define|list|ul|ol">
  <!ENTITY % blockcontent "para|note|warning|table|img|%lists;">
  <!ENTITY % complexcontent "%textcontent;|%blockcontent;">

<!ELEMENT answer (%complexcontent;)*>

I am writing an XSL transform to output HTML, and the template for <answer>
needs to take into consideration whether
a) all the children of answer are textcontent;
b) all the children of answer are blockcontent;
c) there is a mixture of textcontent and blockcontent children

So my first question is: how could I write these tests in XSL 1?

In the output HTML an <answer> element is going to generate a <div>, and
case (a) will require a <div><p>...</p></div>, while in case (b) a simple
<div>...</div> will suffice.

Case (c) is evidence of a poorly structured document, but the XML is defined
by/validated against a DTD not a schema and so I live with the possibility.
This will require my stylesheet to generate <div>...</div> while wrapping
any sequences of consecutive 'textcontent' children in a <p>...</p>.

That leads to the second question: is there a construct that will do this,
comprehensible by a bear with very little brain, again in XSL 1?

Thanks if you can help

Cheers
Trevor


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