Reputation: 57
I have a XML-File like this:
I think you could understand my XML as a table of Content.
AElement @Name='1'
is entry 1
The first AElement @Name='2'
would be entry 1.1
The AElement @Name='3'
below this AElement @Name='2'
would directly belong to it making them entry 1.1.1, entry 1.1.2 and entry 1.1.3 and entry 1.1.4
The second AElement @Name='2'
would be entry 1.2
and so on..
<ROOT>
<AElement Name="1">
<AElement>
<Child1></Child1>
<Child2>0000</Child2>
</AElement>
</AElement>
<AElement Name="2">
<AElement>
<Child1>E</Child1>
<Child2>1000</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>E</Child1>
<Child2>0100</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>U</Child1>
<Child2>0200</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>E</Child1>
<Child2>0300</Child2>
</AElement>
</AElement>
<AElement Name="2">
<AElement>
<Child1>E</Child1>
<Child2>2000</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>N</Child1>
<Child2>0400</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>E</Child1>
<Child2>0500</Child2>
</AElement>
</AElement>
<AElement Name="2">
<AElement>
<Child1>E</Child1>
<Child2>3000</Child2>
</AElement>
</AElement>
<AElement Name="2">
<AElement>
<Child1>U</Child1>
<Child2>4000</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>E</Child1>
<Child2>0600</Child2>
</AElement>
</AElement>
<AElement Name="4">
<AElement>
<Child1>E</Child1>
<Child2>0010</Child2>
</AElement>
</AElement>
</ROOT>
Wanted result:
For each AElement
a table should be created
where for each AElement
of the following step
if AElement/Child1='E'
of that AElement
a table-row and table-cell should be created
this table-cell shall than be filled with the value of AElement/Child2
of that AElement
.
However this table should only be created IF there are any AElement
of the following step with AElement/Child1='E'
.
EXAMPLE
For each AElement @Name='2'
a table should be created
where for each AElement @Name='3'
that belongs to AElement @Name='2'
if AElement/Child1='E'
of AElement @Name='3'
a table-row and table-cell should be created
this table-cell shall than be filled with the value of AElement/Child2
of that AElement @Name='3'
.
So this:
<AElement Name="2">
<AElement>
<Child1>U</Child1>
<Child2>4000</Child2>
</AElement>
</AElement>
<AElement Name="3">
<AElement>
<Child1>E</Child1>
<Child2>0600</Child2>
</AElement>
</AElement>
<AElement Name="4">
<AElement>
<Child1>E</Child1>
<Child2>0010</Child2>
</AElement>
</AElement>
becomes this:
<fo:block>STEP 2 4000</fo:block>
<fo:table>
<fo:table-row>
<fo:table-cell>
<fo:block>
0600
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table>
BUT this:
<AElement Name="2">
<AElement>
<Child1>E</Child1>
<Child2>3000</Child2>
</AElement>
</AElement>
<AElement Name="2">
<AElement>
<Child1>U</Child1>
<Child2>4000</Child2>
</AElement>
</AElement>
would NOT create a table because STEP 3000 has no following AElements @Name='3'
.
<fo:root>
<fo:block>STEP 1 0000</fo:block>
<fo:table>
<fo:table-row>
<fo:table-cell>
<fo:block>
1000
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>
2000
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>
3000
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table>
<fo:block>STEP 2 1000</fo:block>
<fo:table>
<fo:table-row>
<fo:table-cell>
<fo:block>
0100
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>
0300
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table>
<fo:block>STEP 2 2000</fo:block>
<fo:table>
<fo:table-row>
<fo:table-cell>
<fo:block>
0500
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table>
<fo:block>STEP 2 4000</fo:block>
<fo:table>
<fo:table-row>
<fo:table-cell>
<fo:block>
0600
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table>
<fo:block>STEP 3 0600</fo:block>
<fo:table>
<fo:table-row>
<fo:table-cell>
<fo:block>
0010
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table>
</fo:root>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
>
<xsl:output indent="yes" />
<!-- all <AElement> we consider for output, indexed by their @Name and group ID -->
<xsl:key name="AElementGroup1" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '1'][1]))
" />
<xsl:key name="AElementGroup2" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '2'][1]))
" />
<xsl:key name="AElementGroup3" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '3'][1]))
" />
<xsl:key name="AElementGroup4" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '4'][1]))
" />
<xsl:key name="AElementGroup5" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '5'][1]))
" />
<xsl:key name="AElementGroup6" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '6'][1]))
" />
<xsl:key name="AElementGroup7" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '7'][1]))
" />
<xsl:template match="/">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master
master-name="DIN-A4-Landscape"
page-height="210mm"
page-width="297mm"
margin-left="1mm"
margin-right="1mm"
margin-top="1mm"
margin-bottom="1mm"
>
<fo:region-body margin="1mm"/>
<fo:region-before region-name="header" extent="5mm"/>
<fo:region-after region-name="footer" extent="15mm"/>
<fo:region-start region-name="left" extent="5mm"/>
<fo:region-end region-name="right" extent="5mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="DIN-A4-Landscape">
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="ink">
<xsl:with-param name="Now" select="1"/>
</xsl:call-template>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template name="ink">
<xsl:param name="Now"/>
<xsl:param name="Next" select="$Now + 1"/>
<xsl:param name="Ende" select="3"/>
<xsl:apply-templates select="ROOT">
<xsl:with-param name="nowStep" select="$Now"/>
<xsl:with-param name="nextStep" select="$Next"/>
</xsl:call-template>
<xsl:if test="$Ende > $Now">
<xsl:call-template name="ink">
<xsl:with-param name="Now" select="$Now + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="ROOT">
<xsl:param name="nowStep"/>
<xsl:param name="nextStep"/>
<xsl:for-each select="AElement[@Name = $nowStep]">
<xsl:variable name="groupId" select="generate-id()" />
<xsl:variable name="groupKey" select="concat($nextStep, '|', $groupId)" />
<fo:block><xsl:value-of select="concat('STEP ' , $nowStep)" /></fo:block>
<xsl:variable name="group">
<xsl:value-of select="concat('InternalElementGroup',$nowStep)"/>
</xsl:variable>
<xsl:if test="count(key($group, $groupKey))>=1">
<fo:table page-break-after="always">
<fo:table-body>
<xsl:choose>
<xsl:when test="$nowStep = 1">
<xsl:apply-templates select="key('AElementGroup1', $groupKey)/AElement" />
</xsl:when>
<xsl:when test="$nowStep = 2">
<xsl:apply-templates select="key('AElementGroup2', $groupKey)/AElement" />
</xsl:when>
<xsl:when test="$nowStep = 3">
<xsl:apply-templates select="key('AElementGroup3', $groupKey)/AElement" />
</xsl:when>
</xsl:choose>
</fo:table-body>
</fo:table>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="AElement/AElement">
<fo:table-row>
<fo:table-cell>
<fo:block><xsl:value-of select="Child2"/></fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
</xsl:transform>
This code by @Tomalak returns the wanted result.
Upvotes: 1
Views: 121
Reputation: 338396
It's not 100% clear what your intended output is since you only give a single example and no further context.
I am making the following guesses:
<AElement Name="1">
, each of those starts a new "section".<fo:block>STEP N</fo:block>
, where N is the @Name of each <AElement>
.@Name = '2'
, and so on). This makes a total of 8 groups (N <= 8).<fo:table>
with M rows where M is the number of <AElement>
items that have the right @Name and contain a <Child1>E</Child1>
.So the general order of processing would be
AElement[@Name = '1']
<fo:block>STEP N</fo:block>
<fo:table> with M rows for group (N+1) </fo:table>
I do not have a realistic 5000-element input XML to test with, but here is how I would solve this in XSLT 1.0.
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
>
<xsl:output indent="yes" />
<!-- all <AElement> we consider for output, indexed by their @Name and group ID -->
<xsl:key name="AElementGroup" match="AElement[AElement/Child1 = 'E']" use="
concat(@Name, '|', generate-id(preceding-sibling::AElement[@Name = '1'][1]))
" />
<!-- some random nodes, so we have something to count with -->
<xsl:variable name="counter" select="(//node())[position() < 9]" />
<xsl:template match="/">
<fo:root>
<xsl:apply-templates select="ROOT" />
</fo:root>
</xsl:template>
<xsl:template match="ROOT">
<xsl:for-each select="AElement[@Name = '1']">
<xsl:variable name="groupId" select="generate-id()" />
<xsl:for-each select="$counter">
<xsl:variable name="groupKey" select="concat(position() + 1, '|', $groupId)" />
<fo:block><xsl:value-of select="concat('STEP ' , position())" /></fo:block>
<fo:table>
<fo:table-body>
<xsl:apply-templates select="key('AElementGroup', $groupKey)/AElement" />
</fo:table-body>
</fo:table>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="AElement/AElement">
<fo:table-row>
<fo:table-cell>
<fo:block><xsl:value-of select="Child2"/></fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
</xsl:transform>
Before I go into further detail about the suggested solution, please give some feedback on whether this captures your task in the first place.
Upvotes: 1