I have a group of sibling elements in an xml document that need to be processed with XSLT into a table, actually I'm using Apache FOP to transform it into a pdf. Table rows need to be created whenever one of two types of elements are encountered. The cells in the row consist of the element that causes the row to be created in the first cell and then the following siblings in following cells until the next element that causes a row to be created. Here is some example xml to explain it better:
<pre class="prettyprint"><code class="language-xml"> <reqpers> <person man="Four"/> <person man="A" id="pers_a"/> <perscat category="Recovery Supervisor"/> <person man="B" id="pers_b"/> <perscat category="Ground Personnel"/> <asrequir/> <perscat category="As Required Category"/> <trade>Bill Collector</trade> <person man="C" id="pers_c"/> <perscat category="Ground Personnel"/> <perskill skill="sk01"/> <trade>welder</trade> <esttime>.5 hr</esttime> <asrequir/> <perscat category="2nd Required Category"/> <esttime>4 days</esttime> <person man="D" id="pers_d"/> <perscat category="Rides in Chase Vehicle"/> <perskill skill="sk02"/> <person man="E"/> <perscat category="Jack of all Trades"/> <trade>engine mechanic</trade> </reqpers> </code> </pre>
A row needs to be created for each person or asrequir element, then the cells for the row filled with the group of siblings between these elements. I have looked at a lot of examples including: How to select siblings
and this one:How to select group of siblings
as well as many other's. None of these deal with my specific problem set, which is primarily the fact that there are two following-siblings to deal with in the XPath query. Here is an example of what the table should look like:
<pre class="prettyprint"><code class="language-xml"> <table> <table-header> <table-row> <table-cell> Person </table-cell> <table-cell> Category/Trade </table-cell> <table-cell> Skill level </table-cell> <table-cell> Trade code </table-cell> <table-cell> Estimated time </table-cell> </table-row> </table-header> <table-body> <table-row> <table-cell>Four</table-cell> <table-cell/> <table-cell/> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>A</table-cell> <table-cell>Recovery Supervisor</table-cell> <table-cell/> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>B</table-cell> <table-cell>Ground Personnel</table-cell> <table-cell/> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>As required</table-cell> <table-cell/> <table-cell/> <table-cell>Bill Collector</table-cell> <table-cell/> </table-row> <table-row> <table-cell>C</table-cell> <table-cell>Ground Personnel</table-cell> <table-cell>skill gets converted to string value</table-cell> <table-cell>welder</table-cell> <table-cell>.5 hr</table-cell> </table-row> <table-row> <table-cell>As required</table-cell> <table-cell>2nd Required Category</table-cell> <table-cell/> <table-cell/> <table-cell>4 days</table-cell> </table-row> <table-row> <table-cell>D</table-cell> <table-cell>Rides in Chase Vehicle</table-cell> <table-cell>skill level</table-cell> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>E</table-cell> <table-cell>Jack of all Trades</table-cell> <table-cell/> <table-cell>engine mechanic</table-cell> <table-cell/> </table-row> </table-body> </table> </code> </pre>
Also for completeness this is the schema snippet for the xml I'm processing:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" <xs:element name="reqpers" type="reqpersType"/> <xs:complexType name="reqpersType"> <xs:sequence> <xs:element minOccurs="0" ref="applic"/> <xs:sequence maxOccurs="unbounded"> <xs:choice> <xs:element ref="asrequir"/> <xs:element ref="person"/> </xs:choice> <xs:sequence minOccurs="0"> <xs:element ref="perscat"/> <xs:element minOccurs="0" ref="perskill"/> <xs:element minOccurs="0" ref="trade"/> <xs:element minOccurs="0" ref="esttime"/> </xs:sequence> </xs:sequence> </xs:sequence> <xs:attribute ref="refapplic"/> <xs:attributeGroup ref="bodyatt"/> <xs:attributeGroup ref="cntlcontent"/> </xs:complexType>
Here is an example of the xsl code that is the closest I've come, but it only works if there are person siblings, once the asrequir are added things fall apart. I basically repeat this code for each of the cells, replacing the perscat with the element I'm expecting.
<xsl:for-each select="person | asrequir"> <fo:table-row> <fo:table-cell text-align="left" padding-before="1mm" padding-after="1mm" padding-left="1mm" padding-right="1mm"> <fo:block font-size="10pt"> <xsl:value-of select="self::person/@man"/> </fo:block> </fo:table-cell> <fo:table-cell text-align="left" padding-before="1mm" padding-after="1mm" padding-left="1mm" padding-right="1mm"> <xsl:variable name="perscatSib" select="following-sibling::perscat"/> <xsl:variable name="perscatPrec" select="following-sibling::person[1]/preceding-sibling::perscat"/> <fo:block font-size="10pt"> <xsl:choose> <xsl:when test="$perscatSib[count(. | $perscatPrec) = count($perscatPrec)]"> <xsl:value-of select="$perscatSib[count(. | $perscatPrec) = count($perscatPrec)]/@category"/> </xsl:when> <xsl:otherwise> <xsl:if test="preceding-sibling::person[1] and not(following-sibling::person[1])"> <xsl:value-of select="following-sibling::perscat[1]/@category"/> </xsl:if> </xsl:otherwise> </xsl:choose> </fo:block> </fo:table-cell> <continues with the rest of the cells../>
This is the table I'm producing:
<pre class="prettyprint"><code class="language-xml"> <table> <table-header> <table-row> <table-cell> Person </table-cell> <table-cell> Category/Trade </table-cell> <table-cell> Skill level </table-cell> <table-cell> Trade code </table-cell> <table-cell> Estimated time </table-cell> </table-row> </table-header> <table-body> <table-row> <table-cell>Four</table-cell> <table-cell/> <table-cell/> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>A</table-cell> <table-cell>Recovery Supervisor</table-cell> <table-cell/> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>B</table-cell> <table-cell>Ground Personnel</table-cell> <table-cell/> **<table-cell>BillCollector</table-cell>** <table-cell/> </table-row> <table-row> <table-cell>As required</table-cell> <table-cell/> <table-cell/> <table-cell>Bill Collector</table-cell> <table-cell/> </table-row> <table-row> <table-cell>C</table-cell> <table-cell>Ground Personnel</table-cell> <table-cell>skill gets converted to string value</table-cell> <table-cell>welder</table-cell> <table-cell>.5 hr</table-cell> </table-row> <table-row> <table-cell>As required</table-cell> <table-cell>2nd Required Category</table-cell> <table-cell/> <table-cell/> <table-cell>4 days</table-cell> </table-row> <table-row> <table-cell>D</table-cell> <table-cell>Rides in Chase Vehicle</table-cell> <table-cell>skill level</table-cell> <table-cell/> <table-cell/> </table-row> <table-row> <table-cell>E</table-cell> <table-cell>Jack of all Trades</table-cell> <table-cell/> <table-cell>engine mechanic</table-cell> <table-cell/> </table-row> </table-body> </table> </code> </pre>
The table cell with ** around it should not have data in it.. It adds the value from the following asrequir which is not what I want..
I have tried adding following-sibling::person['1']|asrequir['1'] but the asrequir is always true so I get extra siblings in places I don't want them. Any insight or suggestions on how to solve this problem would be very much appreciated. I suspect I need to use a grouping of some sort or a key set, but am not sure how to implement those. While not a newbie to XSLT and XPath, I'm not an expert.