xslt how to fill missing numbers - xslt

This is the input xml
<?xml version="1.0" encoding="UTF-8"?>
<record>
<Tubes>
<Tube carousel_pos="1" tube_pos="1">
<TubeID>FLW140000293A0101</TubeID>
<markers>
<marker position="4">MSIGG1-PC55</marker>
<marker position="9">MSIGG1-PB</marker>
<marker position="8">MSIGG1-APCAF750</marker>
<marker position="10">CD45-KO</marker>
</markers>
</Tube>
<Tube carousel_pos="2" tube_pos="2">
<TubeID>FLW140000293A0102</TubeID>
<markers>
<marker position="4">CD3-PC55</marker>
<marker position="9">CD8-PB</marker>
<marker position="8">CD4-APCAF750</marker>
<marker position="10">CD45-KO</marker>
</markers>
</Tube>
</Tubes>
</record>
This is what it should output
<p num="1">Empty</p>
<p num="2">Empty</p>
<p num="3">Empty</p>
<p num="4">CD3-PC55</p>
<p num="5">Empty</p>
<p num="6">Empty</p>
<p num="7">Empty</p>
<p num="8">CD4-APCAF750</p>
<p num="9">CD8-PB</p>
<p num="10">CD45-KO</p>
This is my xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" exclude-result-prefixes="#all"/>
<xsl:template match="/record/Tubes/Tube">
<xsl:for-each select="markers/marker">
<xsl:sort select="#position" data-type="number"/>
<xsl:choose>
<xsl:when test="position() < #position">
<xsl:call-template name="routine">
<xsl:with-param name="PC" select="position()"/>
<xsl:with-param name="PT" select="#position"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template name="routine">
<xsl:param name="PC"/>
<xsl:param name="PT"/>
</xsl:template>
</xsl:stylesheet>

If you are able to use XSLT 2.0, as your current XSLT is showing, you should be able to make use of an incrementing loop, like so
<xsl:for-each select="1 to 10">
First create a variable holding all the marker elements, the get the maximum #position attribute
<xsl:variable name="markers" select="markers/marker" />
<xsl:variable name="max" select="max($markers/#position)" />
Then, you have your xsl:for-each loop like this:
<xsl:for-each select="1 to xs:integer($max)">
To check if the element exists for each position is then quite straight-forward using the previous markers variable
<xsl:variable name="position" select="position()" />
<xsl:choose>
<xsl:when test="$markers[#position = $position]">
Try this XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="#all">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/record/Tubes/Tube">
<xsl:variable name="markers" select="markers/marker" />
<xsl:variable name="max" select="max($markers/#position)" />
<div>
<xsl:for-each select="1 to xs:integer($max)">
<xsl:variable name="position" select="position()" />
<p num="{$position}">
<xsl:choose>
<xsl:when test="$markers[#position = $position]">
<xsl:value-of select="$markers[#position = $position]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>Empty</xsl:text>
</xsl:otherwise>
</xsl:choose>
</p>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
This produces the following output (This assumed your wanted to do it separately by each tube element:
<div>
<p num="1">Empty</p>
<p num="2">Empty</p>
<p num="3">Empty</p>
<p num="4">MSIGG1-PC55</p>
<p num="5">Empty</p>
<p num="6">Empty</p>
<p num="7">Empty</p>
<p num="8">MSIGG1-APCAF750</p>
<p num="9">MSIGG1-PB</p>
<p num="10">CD45-KO</p>
</div>
<div>
<p num="1">Empty</p>
<p num="2">Empty</p>
<p num="3">Empty</p>
<p num="4">CD3-PC55</p>
<p num="5">Empty</p>
<p num="6">Empty</p>
<p num="7">Empty</p>
<p num="8">CD4-APCAF750</p>
<p num="9">CD8-PB</p>
<p num="10">CD45-KO</p>
</div>

Related

xsl numbering with same elements next to

I need to format some xml data with the following structure
<list>
<item>
Test
</item>
<item>
Testt
</item>
<or-item>
TestOr
</or-item>
<or-item>
TestOrr
</or-item>
<item>
Testtt
</item>
<or-item>
TestOrrr
</or-item>
<item>
Testttt
</item>
</list>
with xsl:number the or-item must be formatted with the second level count on that position. I know it would be better to structure the or-item inside that item but the data is given like that.
I need a way to count the or-item next to the current or-item to calculate the numbering for xsl:number
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.1"
xmlns:axf="http://www.antennahouse.com/names/XSL/Extensions" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output encoding="UTF-8" method="html" indent="yes"/>
<xsl:template match="list">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="item">
<div>
<xsl:number count="item"/>
<xsl:value-of select="."/>
</div>
</xsl:template>
<xsl:template match="or-item">
<div style="padding-left: 10px">
<xsl:number value="count(//or-item)" format="a) "/>
<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
Edit
I am using XSLT 1.1 with xsltproc on linux but 2.0 whould be possible if neccessary
As the target format is HTML, it seems you could rely on creating the appropriate nested HTML ordered lists by using xsl:for-each-group and group-starting-with="item":
<xsl:template match="list">
<ol>
<xsl:for-each-group select="*" group-starting-with="item">
<li>
<xsl:value-of select="."/>
<xsl:where-populated>
<ol>
<xsl:apply-templates select="tail(current-group())"/>
</ol>
</xsl:where-populated>
</li>
</xsl:for-each-group>
</ol>
</xsl:template>
<xsl:template match="or-item">
<li>
<xsl:value-of select="."/>
</li>
</xsl:template>
https://xsltfiddle.liberty-development.net/ejivJrM
That example uses some XSLT/XPath 3 stuff like were-populated and tail but in case that XSLT 2 compatility is needed then it could be replaced by <xsl:if test="subsequence(current-group(), 2)"><ol><xsl:apply-templates select="subsequence(current-group(), 2)"/></xsl:if>.
And of course the use of HTML ordered lists is not necessary, if needed/wanted you could just transform the input to nested divs with the used grouping approach and then in a second step use format-number as you seem to want to do:
<xsl:template match="list">
<xsl:variable name="nested-list">
<xsl:for-each-group select="*" group-starting-with="item">
<xsl:copy>
<xsl:value-of select="."/>
<xsl:copy-of select="tail(current-group())"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:variable>
<div>
<xsl:apply-templates select="$nested-list"/>
</div>
</xsl:template>
<xsl:template match="item">
<div>
<xsl:number/>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="or-item">
<div style="padding-left: 10px">
<xsl:number format="a) "/>
<xsl:value-of select="."/>
</div>
</xsl:template>
https://xsltfiddle.liberty-development.net/ejivJrM/1
You can produce the expected output by simply adjusting the xsl:number instruction:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8"/>
<xsl:template match="/list">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="item">
<div>
<xsl:number/>
<xsl:value-of select="."/>
</div>
</xsl:template>
<xsl:template match="or-item">
<div style="padding-left: 10px">
<xsl:number level="any" from="item" format="a) "/>
<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>

xsl:for-each-group not working as per exception

I have to transform XML tag out of p as separate div but i am unable to do it:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<p>
This is <bold>First</bold> paragraph
<boxed-text>
<sec>
<title>Boxed title</title>
<p>
<list list-type="bullet">
<list-item>
<p>List first para</p>
</list-item>
</list>
</p>
</sec>
</boxed-text>
This is <bold>Second</bold> paragraph
</p>
</root>
XSLT:
<?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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xhtml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<html>
<head>
<title>Title</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="p">
<xsl:choose>
<xsl:when test="descendant::boxed-text">
<xsl:for-each-group select="node()" group-starting-with="node()">
<xsl:choose>
<xsl:when test="self::boxed-text">
<div class="boxed-text">
<xsl:apply-templates select="current-group()"/>
</div>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates select="current-group()"/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="boxed-text">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="p[table-wrap | list]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="list">
<xsl:choose>
<xsl:when test="#list-type[. = 'bullet']">
<ol style="list-style-type:disc;">
<xsl:apply-templates/>
</ol>
</xsl:when>
<xsl:otherwise>
<ol style="list-style-type:none;">
<xsl:apply-templates/>
</ol>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="list-item">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:stylesheet>
Current Output:
<?xml version="1.0" encoding="UTF-8"?><html>
<head>
<title>Title</title>
</head>
<body>
<p class="indent">
This is
</p>
<p class="indent">First</p>
<p class="indent"> paragraph
</p>
<div class="boxed-text">Boxed title
<ol style="list-style-type:disc;">
<li>
<p class="indent">List first para</p>
</li>
</ol>
</div>
<p class="indent">
This is
</p>
<p class="indent">Second</p>
<p class="indent"> paragraph
</p>
</body>
</html>
Excepted output:
<?xml version="1.0" encoding="UTF-8"?><html>
<head>
<title>Title</title>
</head>
<body>
<p class="indent">This is <b>First</b> paragraph</p>
<div class="boxed-text">Boxed title
<ol style="list-style-type:disc;">
<li>
<p class="indent">List first para</p>
</li>
</ol>
</div>
<p class="indent">This is <b>Second</b> paragraph</p>
</body>
</html>
It seems this is rather a job for group-adjacent="boolean(self::boxed-text)":
<xsl:template match="p[descendant::boxed-text]">
<xsl:for-each-group select="node()" group-adjacent="boolean(self::boxed-text)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates select="current-group()"/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
Complete adaption is
<?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"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="html" indent="no" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="p">
<p class="indent">
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="p[descendant::boxed-text]">
<xsl:for-each-group select="node()" group-adjacent="boolean(self::boxed-text)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates select="current-group()"/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="boxed-text">
<div class="boxed-text">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="p[table-wrap | list]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="list">
<ol style="list-style-type:none;">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="list[#list-type[. = 'bullet']]">
<ol style="list-style-type:disc;">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="list-item">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6rewNyg

How to store image width as a number in a variable with xsl

I'm trying to store an image width as a number so I can add a class value to the figure wrapper so I can control the image placement with css, (small, medium, or large image).
This is my xml example:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<section>
<figure class="informalfigure">
<img src="../images/image-fpo-1.png" alt="" width="250" height="800"/>
</figure>
<figure class="informalfigure">
<img src="../images/image-fpo-2.png" alt="" width="650" height="800"/>
</figure>
<figure class="informalfigure">
<img src="../images/image-fpo-3.png" alt="" width="1250" height="800"/>
</figure>
</section>
</root>
This is my xslt:
<?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" exclude-result-prefixes="xs" version="2.0">
<xsl:variable name="small-image">
<xsl:number value="200"/>
</xsl:variable>
<xsl:variable name="medium-image">
<xsl:number value="500"/>
</xsl:variable>
<xsl:variable name="large-image">
<xsl:number value="1000"/>
</xsl:variable>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="figure">
<xsl:variable name="classvalue" select="#class"/>
<xsl:variable name="img-width" select="number(img[#width])"/>
<xsl:choose>
<xsl:when test="number($img-width) > number($large-image)">
<figure class="{$classvalue} large">
<xsl:apply-templates/>
</figure>
</xsl:when>
<xsl:when test="number($img-width) > number($medium-image)">
<figure class="{$classvalue} medium">
<xsl:apply-templates/>
</figure>
</xsl:when>
<xsl:when test="number($img-width) > number($small-image)">
<figure class="{$classvalue} small">
<xsl:apply-templates/>
</figure>
</xsl:when>
<xsl:otherwise>
<figure class="{$classvalue} missedit" width="{$img-width}">
<xsl:apply-templates/>
</figure>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The variable "img-width" is causing the hangup. I clearly have something wrong there. How would I go about storing the image width as a number variable so my test will work? Any help is greatly appreciated. Thanks, Jon
You have to change
<xsl:variable name="img-width" select="number(img[#width])"/>
to
<xsl:variable name="img-width" select="number(img/#width)"/>
because you store in variable "image width" not "image data"

Nested grouping based on a condition

I am learning on xslt group-by. I have a sample xml as below
<?xml version="1.0" encoding="UTF-8"?>
<h1>
<a1>abcd</a1>
<a2>efgh</a2>
<h2>
<b1>IV-3</b1>
<b2>20.00</b2>
<h3>
<c1>VCH</c1>
<c2>1001</c2>
<c3>100.00</c3>
</h3>
</h2>
<h2>
<b1>IV-3</b1>
<b2>50.00</b2>
</h2>
<h2>
<b1>IV-3</b1>
<b2>10.00</b2>
<h3>
<c1>VCH</c1>
<c2>1001</c2>
<c3>300.00</c3>
</h3>
</h2>
<h2>
<b1>IV-3</b1>
<b2>30.00</b2>
</h2>
</h1>
I need to group-by with the b1 value first and sum up b2 values and if h3 node is present , need to group-by c2 and sum up c3 values of that group. My xslt is as below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="h1">
<t1>
<d1>
<xsl:value-of select="a1"/>
</d1>
<d2>
<xsl:value-of select="a2"/>
</d2>
<xsl:for-each-group select="h2" group-by="b1">
<xsl:if test="current-group()/count(h3) > 0 ">
<xsl:for-each-group select="current-group()/h3" group-by="c2">
<t2>
<e1>
<xsl:value-of select="../b1"/>
</e1>
<e2>
<xsl:value-of select="format-number(sum(current-group()/../b2),'#.00')"/>
</e2>
<e3>
<xsl:value-of select="format-number(sum(current-group()/c3),'#.00')"/>
</e3>
</t2>
</xsl:for-each-group>
</xsl:if>
<xsl:if test="current-group()/count(h3) = 0 ">
<t2>
<e1>
<xsl:value-of select="b1"/>
</e1>
<e2>
<xsl:value-of select="format-number(sum(current-group()/b2),'#.00')"/>
</e2>
<e3>
<xsl:value-of select="11111"/>
</e3>
</t2>
</xsl:if>
</xsl:for-each-group>
</t1>
</xsl:template>
</xsl:stylesheet>
The aggrigate value I am getting for e2 is not correct, if h3 is not present. The expected result is
<?xml version="1.0" encoding="UTF-8"?>
<t1 xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<d1>abcd</d1>
<d2>efgh</d2>
<t2>
<e1>IV-3</e1>
<e2>30.00</e2>
<e3>400.00</e3>
</t2>
<t2>
<e1>IV-3</e1>
<e2>80.00</e2>
<e3>11111</e3>
</t2>
</t1>
Thanks for the help!
You want to group on two values at once, so you must change your group-by selector.
From
<xsl:for-each-group select="h2" group-by="b1">
To
<xsl:for-each-group select="h2" group-by="concat(b1, '|', boolean(h3))">
Apart from that your code works.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="h1">
<t1>
<d1>
<xsl:value-of select="a1" />
</d1>
<d2>
<xsl:value-of select="a2" />
</d2>
<xsl:for-each-group select="h2" group-by="concat(b1, '|', boolean(h3))">
<xsl:if test="current-group()/h3">
<xsl:for-each-group select="current-group()/h3" group-by="c2">
<t2>
<e1>
<xsl:value-of select="../b1" />
</e1>
<e2>
<xsl:value-of select="format-number(sum(current-group()/../b2), '#.00')" />
</e2>
<e3>
<xsl:value-of select="format-number(sum(current-group()/c3), '#.00')" />
</e3>
</t2>
</xsl:for-each-group>
</xsl:if>
<xsl:if test="not(current-group()/h3)">
<t2>
<e1>
<xsl:value-of select="b1" />
</e1>
<e2>
<xsl:value-of select="format-number(sum(current-group()/b2), '#.00')" />
</e2>
<e3>
<xsl:value-of select="11111" />
</e3>
</t2>
</xsl:if>
</xsl:for-each-group>
</t1>
</xsl:template>
</xsl:stylesheet>
This stylesheet produces the wanted output
<t1 xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<d1>abcd</d1>
<d2>efgh</d2>
<t2>
<e1>IV-3</e1>
<e2>30.00</e2>
<e3>400.00</e3>
</t2>
<t2>
<e1>IV-3</e1>
<e2>80.00</e2>
<e3>11111</e3>
</t2>
</t1>
Notes
boolean(h3) emits the strings 'true' or 'false' depending on the existence of <h3> elements
in the same manner, your <xsl:if> tests can be simplified to current-group()/h3 and not(current-group()/h3), respectively (the empty node set evaluates to false)
you should declare exclude-result-prefixes="xs fn" to remove the unnecessary namespace declarations from the output document.

XSLT increment variable

I have the following XML
<data>
<records>
<record name="A record">
<info>A1</info>
<info>A2</info>
</record>
<record name="B record"/>
<record name="C record">
<info>C1</info>
</record>
</records>
</data>
how can I transform into following output, the problem is how can I count between on record, and record/info?
<div id="1">
<p>A record</p>
<span id="1">A1</span>
<span id="2">A2</span>
</div>
<div id="2">
<p>C record</p>
<span id="3">C1</span>
</div>
Solution 1. Fine grained traversal. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="records">
<xsl:apply-templates select="*[1]"/>
</xsl:template>
<xsl:template match="record"/>
<xsl:template match="record[node()]">
<xsl:param name="pRecordNum" select="1"/>
<xsl:param name="pInfoNum" select="1"/>
<div id="{$pRecordNum}">
<xsl:apply-templates select="#*|*[1]">
<xsl:with-param name="pInfoNum" select="$pInfoNum"/>
</xsl:apply-templates>
</div>
<xsl:apply-templates select="following-sibling::record[node()][1]">
<xsl:with-param name="pRecordNum" select="$pRecordNum +1"/>
<xsl:with-param name="pInfoNum" select="$pInfoNum + count(info)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="info">
<xsl:param name="pInfoNum"/>
<span id="{$pInfoNum}">
<xsl:value-of select="."/>
</span>
<xsl:apply-templates select="following-sibling::info[1]">
<xsl:with-param name="pInfoNum" select="$pInfoNum +1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="#name">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
Output:
<div id="1">
<p>A record</p>
<span id="1">A1</span>
<span id="2">A2</span>
</div>
<div id="2">
<p>C record</p>
<span id="3">C1</span>
</div>
Solution 2: preceding axe. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="record"/>
<xsl:template match="record[node()]">
<div id="{count(preceding-sibling::record[node()])+1}">
<xsl:apply-templates select="#*|*"/>
</div>
</xsl:template>
<xsl:template match="info">
<span id="{count(preceding::info)+1}">
<xsl:value-of select="."/>
</span>
</xsl:template>
<xsl:template match="#name">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
Solution 3: With fn:position() and preceding axe. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="records">
<xsl:apply-templates select="record[node()]"/>
</xsl:template>
<xsl:template match="record">
<div id="{position()}">
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="info">
<span id="{count(preceding::info)+1}">
<xsl:value-of select="."/>
</span>
</xsl:template>
<xsl:template match="#name">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
Note: You need a explict pull style.
Edit: Missed any level numbering for span/#id.
There is a short way to do this in XSLT. Use the <xsl:number> instruction:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="record[info]">
<xsl:variable name="vPos">
<xsl:number count="record[info]"/>
</xsl:variable>
<div id="{$vPos}">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="info">
<xsl:variable name="vPos">
<xsl:number from="/" level="any" count="info" />
</xsl:variable>
<span id="{$vPos}"><xsl:apply-templates/></span>
</xsl:template>
<xsl:template match="record[not(info)]"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<data>
<records>
<record name="A record">
<info>A1</info>
<info>A2</info>
</record>
<record name="B record"/>
<record name="C record">
<info>C1</info>
</record>
</records>
</data>
the wanted, correct result is produced:
<data>
<records>
<div id="1">
<span id="1">A1</span>
<span id="2">A2</span>
</div>
<div id="2">
<span id="3">C1</span>
</div>
</records>
</data>