counting specific elements
Added by Mark Hutchinson 8 days ago
I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<zap>
<row>
<entry>
<p CC="1">TOPIC #1 - Unordered Lists</p>
</entry>
<entry>
<p CC="1">asdfasdf</p>
<ul CC="2">
<li index="1">UL Level 1 AAA</li>
<li index="2">UL Level 1. BBB</li>
<ul CC="3">
<li index="1">UL Level 2. CCC</li>
<li index="2">UL Level 2 DDD</li>
<p CC="4">zxcvzxcv</p>
<p CC="5">qwerqwer</p>
<ul CC="6">
<li index="1">UL Level 3 EEE</li>
<li index="2">UL Level 3 FFF</li>
</ul>
<li index="3">UL Level 2 GGG</li>
</ul>
<li index="3">UL Level 1 HHH</li>
</ul>
</entry>
</row>
<row>
<entry>
<p CC="1">TOPIC #2 - Ordered Lists</p>
</entry>
<entry>
<ol CC="1">
<li index="1" value="1">OL Level 1. AAA</li>
<li index="2" value="2">OL Level 1. BBB</li>
<ol CC="2">
<li index="1" value="1">OL Level 2. CCC</li>
<li index="2" value="2">OL Level 2. DDD</li>
<ol CC="3">
<li index="1" value="1">OL Level 3 EEE</li>
<ol CC="4">
<li index="1" value="1">OL Level 4 FFF</li>
<ol CC="5">
<li index="1" value="1">OL Level 5 GGG</li>
</ol>
</ol>
</ol>
<li index="3" value="3">OL Level 2. HHH</li>
</ol>
<li index="3" value="3">OL Level 1. III</li>
</ol>
</entry>
</row>
</zap>
The @CC is the value I'm -trying- to achieve using the following XSLT.
NOTE: the code should add a @c
attribute to every <p>
, <ul>
and <ol>
which counts the sequence within each <entry>
. i.e. the first of these elements should result in @c="1"
, the next <p>
, <ul>
or <ul>
would be @c="2"
etc.
The count should continue down through all nested content within each <entry>
. other elements, like <li>
should NOT be counted.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="entry">
<xsl:element name="entry">
<xsl:copy-of select="@* except @written"/>
<xsl:apply-templates select="node()" mode="count"/>
</xsl:element>
</xsl:template>
<xsl:template match="*" mode="count">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="text()" mode="count">
<xsl:copy-of select="."/>
</xsl:template>
<!-- Mode to count and set the c attribute -->
<xsl:template match="p | ul | ol" mode="count">
<xsl:variable name="count" select="count(preceding::p[ancestor::entry[1]] | preceding::ul[ancestor::entry[1]] | preceding::ol[ancestor::entry[1]]) + 1"/>
<xsl:element name="{name()}">
<xsl:attribute name="c" select="$count"/>
<xsl:copy-of select="@* except @written"/>
<xsl:apply-templates select="node()" mode="count"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I have tried many alternatives and they result in worse results. I'm struggling to see why this is so difficult to achieve - although I'm happy to accept that my XSLT skills may be lacking. ... but so are several AI models that I've tried (CoPilot, chatGPT.4o, ...)
File versions of above attached. Any clues, guidance, solutions would be greatly appreciated - this has been driving me nuts for days.
tab-list.xsl (1.18 KB) tab-list.xsl | XSLT 3 | ||
tab-list.xml (1.28 KB) tab-list.xml | input XML | ||
tab-list-out.xml (1.36 KB) tab-list-out.xml | output generated by input and XSLT |
Replies (6)
Please register to reply
RE: counting specific elements - Added by Mark Hutchinson 8 days ago
Apoligies. I hit post before previewing so XML code didn't show up. Attached files were correct.
RE: counting specific elements - Added by Martin Honnen 8 days ago
Consider to at least provide a clean follow-up where we can read complete, meaningful sentences and not broken sentences like which counts the sequence within each
.
Now that you know about preview, start using the editor's toolbar so that you should be able to try to first communicate what you want to achieve and what you are struggling with, it is hard to guess from a sample having CC
attributes and a description like The @CC is the value I'm -trying- to achieve
what your XSLT is supposed to do.
Note that XSLT has xsl:number
and XSLT 3 supported by Saxon since 9.8 additionally has accumulators. These are general hints as to which features of XSLT might be helpful to count/number specific elements, I am afraid I have not been able to grasp which parts of the input are supposed to be transformed in what kind of output. A wild guess for c
would be
<xsl:template match="p | ul | ol">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="c">
<xsl:number level="any" count="p | ul | ol" from="entry"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
So please provide a clean description of the input and the wanted output and the rules to number or count elements, then I am sure we can help you with the XSLT 3. Unfortunately your current post has gone wrong, the forum probably doesn't allow you to edit that stuff to correct it but you can certainly add more than "Apoligies. I hit post before previewing so XML code didn't show up. Attached files were correct." in another attempt to provide a clearer description.
RE: counting specific elements - Added by Martin Honnen 8 days ago
Just use <xsl:mode on-no-match="shallow-copy"/>
for the built-in identity transformation for the rest, no need for any more templates/modes. I think I got it now that the existing CC
are just supposed to indicate what you want for output/create for the c
attribute, then I hope the demo using above's suggestion helps.
RE: counting specific elements - Added by Mark Hutchinson 8 days ago
As soon as I added the xml<xsl:mode on-no-match="shallow-copy"/>
all the @c attributes lit up and 100% correct.
Thank you so much. I can, at last, get a good night's sleep ;) Although I have to get this working in the larger, more complex XSLT but I'm sure its isolated enough that it will work. Thank you again Martin!
RE: counting specific elements - Added by Mark Hutchinson 8 days ago
AND it works in my main XSLT :D
RE: counting specific elements - Added by Michael Kay 7 days ago
the forum probably doesn't allow you to edit that stuff
Yes, it does allow you to edit a post. I've done it for you. Using <xsl:number level="any"/>
is the right tool for the job. I think you're being a bit ambitious hoping that AI tools will give you the answer -- but I'm a bit old-fashioned, I like to understand the code I'm writing and the language I'm writing it in.
Please register to reply