Number the nodes at the end of nested <xsl:for-each> - xslt

I have an XSLT transformation with several nested <xsl:for-each> and <xsl:apply-templates>.
Now i need to number the nodes at the end of this for-each and apply-templates. Everything I tried just numbered the iterations on an level of for-each (e.q. 1,2,3,4,1,2,1,2,3,4 but I need 1,2,3,4,5,6,7,8,9,10)
(I'm pretty inexperienced with XSLT, but attempted to solve this problem with different variants of <xsl:number> and position().)
test.xml
<A>
<B>
<C/>
<C/>
<C/>
<C/>
</B>
<B>
<C/>
<C/>
</B>
</A>
text.xsl:
<xsl:template match="A">
<xsl:for-each select="B">
<xsl:for-each select="C">
<xsl:number/>,
</xsl:for-each>
</xsl:for-each>
</xsl:template>
test.out
1,2,3,4,1,2,
I would like to have
1,2,3,4,5,6
EDIT: This example is to simple, it works with <xsl:number level="any" />. I first have to make a better example

<xsl:number value="count(preceding::C) + 1"/><xsl:if test="following::C">,</xsl:if>
(or something similar) should do it.

Try:
<xsl:template match="A/B/C">
<xsl:value-of select="position()" />
</xsl:template>
position() always returns the position of the current node in the batch of nodes that is being processed at the moment. Your solution:
<xsl:template match="A">
<xsl:for-each select="B">
<xsl:for-each select="C">
<xsl:number/>,
</xsl:for-each>
</xsl:for-each>
</xsl:template>
Processes four batches of nodes:
One batch of <A> nodes. They go from position 1 to 1.
One batch of <B> nodes. They go from position 1 to 2.
Two Batches of <C> nodes. They go from position 1-4 and 1-2
While my solution processes, by selecting them directly:
One batch of <C> nodes. They go from position 1-6

You can increment the variable:
<xsl:template match="A">
<xsl:variable name="count" select="1"/>
<xsl:for-each select="B">
<xsl:for-each select="C">
<xsl:variable name="count" select="$count + 1"/>
<xsl:value-of select="count" />,
</xsl:for-each>
</xsl:for-each>
</xsl:template>

Related

Making a tree from delimited elements using XSLT

The question is similar to this one but slightly more complex...
I need to transform a flat XML like
<XML>
<A.W>1</A.W>
<A.X>2</A.X>
<B.Y>3</B.Y>
<B.Z>4</B.Z>
<C>5</C>
</XML>
to a proper tree
<XML>
<A>
<W>1</W>
<X>2</X>
</A>
<B>
<Y>3</Y>
<Z>4</Z>
</B>
<C>5</C>
</XML>
using XSLT
So, the levels of the tree are dot-separated. Ideally with unlimited fold, but could be just the 2-levels one. Thank you!
Try this for starters:
<xsl:template match="XML">
<xsl:for-each-group select="*"
group-adjacent="substring-before(local-name(), '.')">
<xsl:element name="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<xsl:element name="{substring-after(local-name(), '.')}">
<xsl:copy-of select="child::node()"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</xsl:template>

XSLT / conditional for-each with same parent (uncle) node value

I need some help understanding how I can select only certain nodes having same parent (or uncle) element value. My XML looks like this
<shipment>
<goodslines>
<goodsline>
<position>1</position>
<packagenumbers>
<packagenumber>123</packagenumber>
</packagenumbers>
</goodsline>
<goodsline>
<position>1</position>
<packagenumbers>
<packagenumber>456</packagenumbers>
</packagenumbers>
</goodsline>
<goodsline>
<position>2</position>
<packagenumbers>
<packagenumber>789</packagenumbers>
</packagenumbers>
</goodsline>
</goodslines>
</shipment>
and the desired output would be:
123,456
789
So I would need to do for-each to "packagenumber" - level so, that it would take in consideration the "position" - element from upper level
The XSL might be something like this?
<xsl:for-each select="shipment/goodslines/goodsline[some condition here?]/packagenumbers/packagenumber">
<xsl:value-of select="current()"/>
<xsl:if test="not(position() = last())">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:for-each>
Any help would be appreciated.
If your xml always has the same structure you can use this:
<xsl:template match="goodsline[position = preceding-sibling::goodsline[1]/position]">
<xsl:text>,</xsl:text>
<xsl:value-of select="packagenumbers/packagenumber"/>
</xsl:template>
<xsl:template match="goodsline">
<xsl:text>
</xsl:text>
<xsl:value-of select="packagenumbers/packagenumber"/>
</xsl:template>
More specific templates (the one with the condition) will always hit first and have higher priority. There is probably another simple solution with for-each-group.

Get parent node from child and rename it

I have the input bellow and I wrote some xslt that gives me an office with a specific ID but since I get the parent node I also get the tag <e>. My problem is that I don't want to have that <e> tag.
<response>
<offices>
<e>
<id>33701</id>
<name>aa</name>
</e>
.....<e></e>
</offices>
</response>
<xsl:template match="*:response/offices">
<econ:GetOfficesResponse>
<Office>
<xsl:for-each select="e/id">
<xsl:if test="text() = $office_id">
<xsl:copy-of select="parent::node()"/>
</xsl:if>
</xsl:for-each>
</Office>
</econ:GetOfficesResponse>
</xsl:template>
</xsl:stylesheet>
The response that I get:
<econ:GetOfficesResponse>
<Office>
<e>
<id>33701</id>
<name>...</name>
</e>
</Office>
The response that I want:
<econ:GetOfficesResponse>
<Office>
<id>33701</id>
<name>...</name>
</Office>
Can someone please help me with this? I/m using xslt 2.0
It seems like instead of your xsl:for-each you simply want a single <xsl:copy-of select="e[id = $office_id]/*"/>
try this code:
<xsl:template match="*:response/offices">
<econ:GetOfficesResponse>
<Office>
<xsl:for-each select="e/id">
<xsl:if test="text() = $office_id">
<xsl:copy-of select="parent::node()/child::node()"/>
</xsl:if>
</xsl:for-each>
</Office>
</econ:GetOfficesResponse>
</xsl:template>

XSLT Refer to the current node of a template within for-each

how can I refer to the current node matched by a template, within a for-each loop.
<xsl:template match="product[#category='foo']">
<count>
<xsl:for-each select="preceding::product">
<current>
<xsl:value-of select="count(current()/preceding::product)"/>
</current>
<context>
<xsl:value-of select="count(./preceding::product)"/>
</context>
</xsl:for-each>
</count>
</xsl:template>
Both count() functions return the same results with . as well as current().
Is there a way to refer to the matched node of the template, in order to count all the preceding nodes of it, so that the result of the count() function inside <current/> is the same for every for-each step.
Thanks!
Put the current node in a variable....
<xsl:template match="product[#category='foo']">
<xsl:variable name="current" select="." />
<count>
<xsl:for-each select="preceding::product">
<current>
<xsl:value-of select="count($current/preceding::product)"/>
</current>
<context>
<xsl:value-of select="count(./preceding::product)"/>
</context>
</xsl:for-each>
</count>
</xsl:template>
In this example though, you would be better place putting the results of the count in a variable though...
Put the current node in a variable....
<xsl:template match="product[#category='foo']">
<xsl:variable name="count" select="count(preceding::product)" />
<count>
<xsl:for-each select="preceding::product">
<current>
<xsl:value-of select="$count"/>
</current>
<context>
<xsl:value-of select="count(./preceding::product)"/>
</context>
</xsl:for-each>
</count>
</xsl:template>

XSLT how to partition a sequence into sub-sequences

I have an XML file that contains a sequence of strings
<ModsConfigData>
<buildNumber>1393</buildNumber>
<activeMods>
<li>Core</li>
<li>ZhentarFix</li>
<li>WorldPawnGC</li>
<li>HugsLib</li>
<!-- more entries -->
</activeMods>
</ModsConfigData>
And I wish to partition the long list of elements into subsets of a given length -- in my case, lengths of five. The purpose is to then work with the subsets to generate a table structure that is five across and however many rows it takes to consume the full list.
The output I'm wanting to achieve is a subset of BBCode like so:
[table]
[tr]
[td]Core[/td][td]ZhentarFix[/td][td]WorldPawnGC[/td] ...
[/tr]
[tr]
[td] ... (next set of five entries) [/td]
[/tr]
[/table]
It looks like a set of queries to me. There are probably ways to do a for-each loop, and position() mod 5 or position div 5 might be a way to get there. I'm not familiar enough with the grouping instruction to even guess if can be of assistance.
This is what I'm currently using, any improvements welcome.
<xsl:variable name="modsConfigFile" select="document('ModsConfig.xml')"/>
<xsl:variable name="groupSize" select="5"/>
<xsl:template match="/ModsConfigData/activeMods">
<xsl:text>[hr][table]</xsl:text>
<xsl:value-of>
<xsl:apply-templates
select="li[position() mod $groupSize = 1]"/>
</xsl:value-of>
<xsl:text>[/table]</xsl:text>
</xsl:template>
<xsl:template match="li">
<xsl:text>[tr]</xsl:text>
<xsl:apply-templates select=
".|following-sibling::li[position() < $groupSize]"/>
<xsl:text>[/tr]</xsl:text>
<br/>
</xsl:template>
<xsl:template match="li" >
<xsl:text>[td]</xsl:text>
<xsl:value-of select="."/>
<xsl:text>[/td]</xsl:text>
</xsl:template>
</xsl:stylesheet>
This is what I'm currently using,
You can't be "using" that, because it doesn't work. It doesn't work because (among other things):
you cannot place xsl:apply-templates within xsl-value-of; and
you have two templates matching li, with no distinction between them.
To do it the way you have started, you need to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:variable name="groupSize" select="5"/>
<xsl:template match="/ModsConfigData">
<xsl:text>[table]
</xsl:text>
<xsl:apply-templates select="activeMods/li[position() mod $groupSize = 1]"/>
<xsl:text>[/table]</xsl:text>
</xsl:template>
<xsl:template match="li">
<xsl:text> [tr]
</xsl:text>
<xsl:apply-templates select= ". | following-sibling::li[position() < $groupSize]" mode="cell"/>
<xsl:text>
[/tr]
</xsl:text>
</xsl:template>
<xsl:template match="li" mode="cell">
<xsl:text>[td]</xsl:text>
<xsl:value-of select="."/>
<xsl:text>[/td]</xsl:text>
</xsl:template>
</xsl:stylesheet>
When the above is applied to the following test input:
XML
<ModsConfigData>
<buildNumber>1393</buildNumber>
<activeMods>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
<li>F</li>
<li>G</li>
<li>H</li>
<li>I</li>
<li>J</li>
<li>K</li>
<li>L</li>
</activeMods>
</ModsConfigData>
the result will be:
[table]
[tr]
[td]A[/td][td]B[/td][td]C[/td][td]D[/td][td]E[/td]
[/tr]
[tr]
[td]F[/td][td]G[/td][td]H[/td][td]I[/td][td]J[/td]
[/tr]
[tr]
[td]K[/td][td]L[/td]
[/tr]
[/table]