Home. 
.

transparent

transparent

transparent

Altova Mailing List Archives


Re: Help needed: dynamically counting an attribute

From: Jeni Tennison <jeni@---------------->
To:
Date: 7/4/2000 10:28:00 AM
Ali,

>I want to write a stylesheet such that after parsing this XML file, it 
>counts the number
>of those elements which have the same partNum and report that in some way, 

This is a grouping problem: you want to group the parts together, and count
the number of items in each group.  Grouping problems are currently (and
using basic XSLT without any processor extensions) best solved using the
Muenchian method, which involves defining a key that does the grouping
quickly for you.

When you design a key to help you group things together, you have three
variables to set:
* name - a name for the key, anything you like: 'parts' in this case
* match - the things that you want to group: elements with 'partNum'
attributes in this case
* use - the thing that defines the groups: the number of the part in this case

So, the key that you want looks like:

<xsl:key name="parts" match="*[@partNum]" use="@partNum" />

This element is a top-level element: it goes right underneath the
xsl:stylesheet element.  Note that I have assumed within the 'match'
expression that different elements, with different names, might all have
'partNum' attributes, so as well as:

<CPU partNum="1345" />

you might have:

<HDD partNum="5437" />

If the elements that you're interested in are *all* CPUs, then you could use:

<xsl:key name="CPUs" match="CPU" use="@partNum" />

instead.  Indeed, you could define several different keys for each of the
elements that have partNums, if you know those in advance.

Retrieving the groups involves using the key() function.  The first
argument is the name of the key (so 'parts' in this case) and the second
argument is the value of the thing that was used to group the things you're
grouping, so a part number in this case, something like:

  key('parts', '1345')

You can dynamically decide what the part number (or even key name) is.

Getting a list of the part numbers is the slightly tricky bit.  You need to
identify one of the group for each of the groups that you've defined in the
key.  You do this by comparing a node (a 'CPU' element) with the first node
that you get when you use the key value for that node to index into the
key.  So, if the first element that you get when you use the value '1345'
to index into your 'parts' key is the same as the current element, then you
know that it's the first one to appear in the list.  To compare nodes, you
use the generate-id() function.  So, a template matching on the parent of
the CPU elements should look something like:

<xsl:template match="parts">
  <table>
    <tr><th>partNum</th><th>Qty</th></tr>
    <xsl:apply-templates
      select="*[@partNum and
                generate-id(.)=generate-id(key('parts', @partNum))]" />
  </table>
</xsl:template>

This guarantees that the only parts that have templates applied to them are
those that occur first in the list of parts with that particular part
number.  You can then have a template that matches on them and outputs the
rows of your table.  To count the number of parts with that particular part
number, you count the number of elements that are retrieved when you use
that part number to index into the key that you've used to group your
elements:

<xsl:template match="*[@partNum]">
  <tr>
    <!-- first column is the value of the partNum attribute -->
    <td><xsl:value-of select="@partNum" /></td>
    <!-- second column is the number of parts with that partNum -->
    <td><xsl:value-of select="count(key('parts', @partNum))" /></td>
  </tr>
</xsl:template>

This is tested and works in SAXON.  The important things to take out of
this example are how to use the key to group the elements that you're
interested in:

<xsl:key name="parts" match="*[@partNum]" use="@partNum" />

how to retrieve them and count them:

  count(key('parts', @partNum))

and how to retrieve the unique elements so that you can identify what part
numbers are used in your file:

  *[@partNum and
    generate-id(.)=generate-id(key('parts', @partNum))]

>The difficulty that I have is the following: I have different XML files 
>(with similar structure)
>in which, these partNum are different , and may even change in the future, 

I'm not sure whether this means you want to summarise the content of all
these different files at the same time.  If you do, you should take note
that the key() function only indexes nodes in the documents that you are
currently working on.  You can set the current documents using the
document() function, but that's another question :)

I hope this helps,

Jeni

Dr Jeni Tennison
Epistemics Ltd * Strelley Hall * Nottingham * NG8 6PE
tel: 0115 906 1301 * fax: 0115 906 1304 * email: jeni.tennison@xxxxxxxxxxxxxxxx


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


transparent
Print
Mail
Digg
delicious
Disclaimer
.

These Archives are provided for informational purposes only and have been generated directly from the Altova mailing list archive system and are comprised of the lists set forth on www.altova.com/list/index.html. Therefore, Altova does not warrant or guarantee the accuracy, reliability, completeness, usefulness, non-infringement of intellectual property rights, or quality of any content on the Altova Mailing List Archive(s), regardless of who originates that content. You expressly understand and agree that you bear all risks associated with using or relying on that content. Altova will not be liable or responsible in any way for any content posted including, but not limited to, any errors or omissions in content, or for any losses or damage of any kind incurred as a result of the use of or reliance on any content. This disclaimer and limitation on liability is in addition to the disclaimers and limitations contained in the Website Terms of Use and elsewhere on the site.

.
.

transparent

transparent