Search code examples
xsltxslt-2.0

Counting figures in xslt


I am trying to write some xslt that will enumerate all images in my xml(s!)

i am using a key for this so that i can reference the images later in the content, my problem here is i cant figure out how to get a consistent value. Meaning: the xml has references to multiple other documents which i am loading using document(), whenever i "enter" a new document the key is reset, so in documents that have 2 images i get a count of two, in others one

so you can imagine

root doc:

<root>
  <chapter href="chapters/doc1.xml"/>
  <chapter href="chapters/doc2.xml"/>
</root> 

doc1.xml

<root>
  <image id="a"/>
  <image id="b"/>
</root> 

doc2.xml

<root>
  <image id="f"/>
</root> 

i found a old thread here on stackoverflow that i think goes kind of in my direction: XSLT: create 'key' across multiple documents? but i was not able to make it work.

what is currently working: i have defined a key

<xsl:key name="image" match="image" use="@id"/>

that im using later like this

 key value for image:<xsl:value-of select="count(key('image', @id)/preceding::image) + 1"/>

this always produces the mentioned result (for doc1 i get "key value for image:1 key value for image:2" bur for doc2 i get "key value for image:1" again and here i would expect a 3)

trying to adapt to the linked post i have tried to load all documents using collection and at create a new key hoping it would yield me at least all images for the start

<xsl:key name="images" match="image" use="all"/>
 <xsl:variable name="location"><xsl:value-of select="$xml-base"/>/chapters?select=*.xml;recurse=yes</xsl:variable>
<xsl:variable name="documents">
  <xsl:value-of select="collection($location)"/>
</xsl:variable>
<xsl:for-each select="collection($location)">
  <xsl:message>
    <xsl:value-of select="./count(key('figure','all'))"/>
  </xsl:message>
</xsl:for-each>

and that always gives me 0 i am not even sure if using key is the right approach here

tldr: how can i enumerate images in xslt across multiple documents?

*edit as i think it was a bit unclear: i have multiple xml inputs imported by a root xml, all of them can contain images i have a final output file that needs to enumerate all images continiously and be able to reference them


Solution

  • That last document has a single image element, therefore that image doesn't have any preceding::image.

    It is also not clear why you need a key at all if you want to count image elements, count(//image) for a single document or count(document(/root/chapter/@href)//image) for using the document function on that first sample or count(collection('chapters?select=*.xml;recurse=yes')//image) for the XSLT 2/3 and Saxon 9 specific collection() call should do.

    <xsl:key name="images" match="image" use="all"/> is meaningless unless any image element has child elements named all.

    Dealing with severals documents is possible but requires some extra effort. If you need general processing and can't push all elements together through a template to exploit position() but somehow need to index all those image elements then perhaps doing <xsl:variable name="images" select="document(/root/chapter/@href)//image"/> and <xsl:variable name="image-ids" select="for $img in $images return generate-id($img)"/> and then simply using <xsl:value-of select="index-of($image-ids, generate-id())"/> in any xsl:template match="image" should work.

    The for $img in $images return generate-id($img) can be written more compactly in XSLT 3 as $images ! generate-id().