Re: [xsl] Element selection based on different overlapping descendant subsets

Subject: Re: [xsl] Element selection based on different overlapping descendant subsets
From: andrew welch <andrew.j.welch@xxxxxxxxx>
Date: Wed, 1 Feb 2006 12:00:37 +0000
On 2/1/06, Trevor Nicholls <trevor@xxxxxxxxxxxxxxxxxx> wrote:
> Hello
>
> I am transforming some poorly designed XML files into documents which
> conform to a relatively simple schema. One of the decisions I need to make
> involves an element called <TABLE>. There are literally thousands of tables
> in my input and literally hundreds of different element types which may be
> descendants of tables (these are nothing like HTML tables!) However certain
> specific table types are discernable based on the family of elements they
> contain, i.e. I can define a set M of element names and if every descendant
> of a particular <TABLE> is in this set then it is an M-TABLE, I can define
> another set N to identify any N-TABLE, and so on. Individual elements may
> appear in more than one set, individual tables may not.
>
> What I am struggling with is how to define this condition in an Xpath. I
> know how to identify any <TABLE> that contains a <Cell> descendant, but
> <Cell> elements may be in more than one table type - it depends on what
> other descendants I can find. Similarly I can identify any <TABLE> that
does
> NOT contain a <Side> descendant, but again there are multiple table types
> that lack a <Side>.
>
> A very simple case can be coded in the following XSL fragment to illustrate
> what I mean:
>
> <xsl:template match="TABLE">
> <xsl:variable name="descs" select="count(descendant::*)" />
> <xsl:variable name="tabx" select="count(descendant::ROW | descendant::CELL
|
> descendant::A | descendant::CODE | descendant::TYPING)" />
> <xsl:choose>
>         <xsl:when test="$descs=$tabx"><xsl:call-template name="tableX"
> /></xsl:when>
>         <xsl:otherwise><xsl:call-template name="tableOther"
> /></xsl:otherwise>
> </xsl:choose>
> </xsl:template>
>
> This works - and would be quite acceptable if I was choosing between two
> types of table and only had half a dozen element names to compare - but it
> doesn't scale very well!
>
> My question: is there a "manageably" concise expression that will let me
> test whether each and every descendant element of the context element is a
> member of a particular set of element names? And similarly (in case this is
> more appropriate for certain types) is there an analogous expression that
> will let me test whether none of the descendant elements of the context
> element may be found in a particular set?
>
> Sorry, I've been struggling with this for several hours and can't find the
> sort of thing I'm after in the FAQ either.

Define each known table type as a key:

<xsl:key name="m-table" match="table[.//ROW][.//CELL][.//A]"
use="generate-id()"/>

<xsl:key name="n-table" match="table[.//TYPING][.//CODE]"
use="generate-id()"/>

The m-table key then contains all the tables that fit the description
of an "M table", as so on.

Then when you process each <TABLE> element, you can easily check what
type of table it is:

<xsl:template match="TABLE">
  <xsl:choose>
    <xsl:when test="key(m-table, generate-id())">
      I'm an m-table
    </xsk:when>
    <xsl:when test="key(n-table, generate-id())">
      I'm an n-table
    </xsk:when>

cheers
andrew

Current Thread