fo:Table to html table - xslt - xslt

I'm trying to write an xslt to generate another xslt that purpose is replacing only all xsl:fo with html tags..XSLT 1
I use CDATA around "xsl" namespace for avoid processing this kind of tag by the xslt. My scope is processing only xsl:fo directive and replace for example :
<fo:table table-layout="fixed" width="100%" font-size="10pt">
<fo:table-column column-width="proportional-column-width(0.65)"/>
<fo:table-column column-width="proportional-column-width(0.35)"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell padding-before="0.5cm"></fo:table-cell>
<fo:table-cell padding-before="0.5cm">
<fo:block>
y
<![CDATA[ --> this is treated as text so i can copy it with <xsl-valueof select="."/>??
<xsl:choose>
<xsl:when test="...xpath'">
<xsl:value-of select="..." />,
</xsl:when>
<xsl:otherwise>
at <xsl:value-of select=..." />,
</xsl:otherwise>
</xsl:choose>]]>
</fo:block>
<fo:block space-before="0.5cm" text-align="center">
x
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
I want traslate fo:table+fo:table-body with table tag, and fo:table-column with td width="..%", fo:table-row with tr.. Td width is not so easy to retrieve because the width property belong to fo:table-column and fo:table-cell handling the tag.
I try to loop fo:table-column when i read a table-cell i'm writing td and calculate the width using the property column-width obtained by precedent tag fo:table-column: i use the position() of tag table-column (first loop) in the fo:table-cell selection
for example here is my xslt tralslator for xsl:fo (above-mentioned):
<xsl:template name="fo-table">
<xsl:param name="font-size" />
<xsl:param name="width" />
<xsl:variable name="cols" select="count(fo:table-column)"/>
<xsl:if test="fo:table-column">
<xsl:variable name="effective-cols" select="count(fo:table-body/fo:table-row/fo:table-cell)"/>
<xsl:if test="$cols = $effective-cols">
<table>
<xsl:for-each select="fo:table-body/fo:table-row">
<tr>
<xsl:for-each select="parent::*/parent::*/fo:table-column">
<xsl:variable name="width-proportional">
<xsl:value-of select="#column-width"/>
</xsl:variable>
<td>
<xsl:attribute name="width">
<xsl:call-template name="getPercentWidth">
<xsl:with-param name="proportional-value-width"><xsl:value-of select="$width-proportional"/></xsl:with-param>
</xsl:call-template>
</xsl:attribute>
abc <xsl:variable name="vPosition"><xsl:value-of select="position()"/></xsl:variable>
<xsl:for-each select="parent::*/fo:table-body/fo:table-row/*[$vPosition]">
<xsl:value-of select="local-name()"/><xsl:text> #10;</xsl:text> <!-- debug-->
<xsl:choose>
<xsl:when test="fo:block">
<xsl:for-each select="fo:block">
<xsl:call-template name="fo-block-table">
<xsl:with-param name="text-align"><xsl:value-of select="#text-align"/></xsl:with-param>
<xsl:with-param name="space-before"><xsl:value-of select="#space-before"/></xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
empty cell
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="fo-block-table">
<xsl:param name="text-align" />
<xsl:param name="space-before" />
<xsl:choose>
<xsl:when test="$text-align">
<div>
<xsl:attribute name="text-align">
<xsl:value-of select="normalize-space($text-align)"/>
</xsl:attribute>
<xsl:apply-templates select="."/>
</div>
</xsl:when>
<xsl:otherwise>
<div>
<xsl:apply-templates select="."/>
</div>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="getPercentWidth">
<xsl:param name="proportional-value-width"/>
<xsl:variable name="width" select="normalize-space($proportional-value-width)"/>
<xsl:variable name="begin"> <xsl:value-of select="string-length(substring-before($width, '('))" /></xsl:variable>
<xsl:variable name="last"> <xsl:value-of select="string-length(substring-before($width,')'))" /></xsl:variable>
<xsl:variable name="val" select="fn:substring($width, $begin, $last)" />
<xsl:variable name="val1" select="substring-after($val,'(')"/>
<xsl:variable name="cent" select="100"/>
<xsl:value-of select="concat(($val1 * $cent),'%')"/>
</xsl:template>
But i cant realize why all td's contains 'y',x and empty when it will belong only to the empty table-cell, seems it reads all fo:block..
<table>
<tr>
<td width="65%">
abc
table-cell #10;
empty cell
table-cell #10;<div>
y
</div>
<div text-align="center">
x
</div>
</td>
<td width="35%">
abc
table-cell #10;
empty cell
table-cell #10;<div>
y
</div>
<div text-align="center">
x
</div>
</td>
</tr>
</table>
I need to obtain:
<table>
<tr>
<td width="65%">
abc
table-cell #10;
empty cell
</td>
<td width="35%">
abc
table-cell #10;
<div>
y
</div>
<div text-align="center">
x
</div>
</td>
</tr>
</table>
if i replace the second loop
xsl : for-each
with
xsl : template
don't match anything!
Maybe *[$vPosition] doesn't work but it works if i replace number like 1 or 2..
What's wrong?
Thanks in advice!
Roby

The problem is with this line...
<xsl:for-each select="parent::*/fo:table-body/fo:table-row/*[$vPosition]">
Or rather, it is due to how the vPosition variable is defined:
<xsl:variable name="vPosition"><xsl:value-of select="position()"/></xsl:variable>
By using xsl:value-of you are actually causing vPosition to be set to a string value, not a numeric value. When you use a string value in a condition, like [*$vPosition] it will always return true if that string is not empty.
Try changing the variable declaration to this, which will set vPosition to be a number
<xsl:variable name="vPosition" select="position()" />

Related

Recursive Template Generating Extra Stuff

I am working with Xml that that has multiple levels where I am attempting to display as nested html tables. The Xml may contain one or more levels of information. The current state is the information from the Xml is shown, but has extra empty tables.
Below is the Xml and template I am using. How should I correct the problem?
<?xml version="1.0" encoding="UTF-8"?>
<ErrorRecord>
<Exception>
<ErrorRecord>Exception calling "Fill" with "2" argument(s): "Divide by zero error encountered."</ErrorRecord>
<WasThrownFromThrowStatement>True</WasThrownFromThrowStatement>
<TargetSite>Void CheckActionPreference(System.Management.Automation.Language.FunctionContext, System.Exception)</TargetSite>
<Message>Exception calling "Fill" with "2" argument(s): "Divide by zero error encountered."</Message>
<Data>System.Collections.ListDictionaryInternal</Data>
<InnerException>
<Error>
<Source>Core .Net SqlClient Data Provider</Source>
<Number>8134</Number>
<State>1</State>
<Class>16</Class>
<Server>.</Server>
<Message>Divide by zero error encountered.</Message>
<Procedure></Procedure>
<LineNumber>1</LineNumber>
</Error>
<ClientConnectionId>cdfed373-14fb-4dd7-bebf-7158695cb2c7</ClientConnectionId>
<Class>16</Class>
<LineNumber>1</LineNumber>
<Number>8134</Number>
<Procedure></Procedure>
<Server>.</Server>
<State>1</State>
<Source>Core .Net SqlClient Data Provider</Source>
<IsTransient>False</IsTransient>
<SqlState></SqlState>
<BatchCommand></BatchCommand>
<ErrorCode>-2146232060</ErrorCode>
<TargetSite>Void OnError(System.Data.SqlClient.SqlException, Boolean, System.Action`1[System.Action])</TargetSite>
<Message>Divide by zero error encountered.</Message>
<Data>System.Collections.ListDictionaryInternal</Data>
<InnerException></InnerException>
<HelpLink></HelpLink>
<HResult>-2146232060</HResult>
<StackTrace> at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryHasMoreRows(Boolean& moreRows)
at System.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more)
at System.Data.SqlClient.SqlDataReader.Read()
at System.Data.Common.DataAdapter.FillLoadDataRow(SchemaMapping mapping)
at System.Data.Common.DataAdapter.FillFromReader(DataSet dataset, DataTable datatable, String srcTable, DataReaderContainer dataReader, Int32 startRecord, Int32 maxRecords, DataColumn parentChapterColumn, Object parentChapterValue)
at System.Data.Common.DataAdapter.Fill(DataSet dataSet, String srcTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, String srcTable)
at CallSite.Target(Closure, CallSite, Object, Object, Object)</StackTrace>
</InnerException>
<HelpLink></HelpLink>
<Source>System.Management.Automation</Source>
<HResult>-2146233087</HResult>
<StackTrace> at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)</StackTrace>
</Exception>
</ErrorRecord>
<xsl:template match = "ErrorRecord">
<xsl:call-template name="ErrorTable">
<xsl:with-param name="n" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="ErrorTable">
<xsl:param name="n" />
<xsl:for-each select="$n/*">
<table class="ErrorDetailTable">
<tr>
<th class="Property"> </th>
<th class="Value"> </th>
</tr>
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">
<tr class="tr-even">
<xsl:call-template name="ErrorCells"/>
</tr>
</xsl:when>
<xsl:otherwise>
<tr class="tr-odd">
<xsl:call-template name="ErrorCells"/>
</tr>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<tr><td colspan="2"> </td></tr>
</table>
</xsl:for-each>
</xsl:template>
<xsl:template name="ErrorCells">
<td class="Property"><xsl:value-of select ="local-name(.)"/></td>
<td class="Value">
<xsl:choose>
<xsl:when test="count(./*) > 0">
<xsl:call-template name="ErrorTable">
<xsl:with-param name="n" select=".." />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="replace_sab">
<xsl:with-param name="s" select="." />
<xsl:with-param name="a" select="'
'" />
<xsl:with-param name="b"><br /></xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:template>
<xsl:template name="replace_sab">
<!-- with string s, replace substring a by string b -->
<!-- s, a and b are parameters determined upon calling -->
<xsl:param name="s" />
<xsl:param name="a" />
<xsl:param name="b" />
<xsl:choose>
<xsl:when test="contains($s,$a)">
<xsl:value-of select="substring-before($s,$a)" />
<xsl:copy-of select="$b" />
<xsl:call-template name="replace_sab">
<xsl:with-param name="s" select="substring-after($s,$a)" />
<xsl:with-param name="a" select="$a" />
<xsl:with-param name="b" select="$b" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I was able to find the issue, which was due to the reference to the current node with the xml. Solution is as below.
<xsl:template match = "ErrorRecord">
<xsl:call-template name="ErrorTable">
<xsl:with-param name="n" select="./Exception" />
</xsl:call-template>
</xsl:template>
<xsl:template name="ErrorTable">
<xsl:param name="n" />
<xsl:for-each select="$n">
<table class="ErrorDetailTable">
<tr>
<th class="Property"> </th>
<th class="Value"> </th>
</tr>
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">
<tr class="tr-even">
<xsl:call-template name="ErrorCells"/>
</tr>
</xsl:when>
<xsl:otherwise>
<tr class="tr-odd">
<xsl:call-template name="ErrorCells"/>
</tr>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<tr><td colspan="2"> </td></tr>
</table>
</xsl:for-each>
</xsl:template>
<xsl:template name="ErrorCells">
<td class="Property"><xsl:value-of select ="local-name(.)"/></td>
<td class="Value">
<xsl:choose>
<xsl:when test="count(./*) > 0">
<xsl:call-template name="ErrorTable">
<xsl:with-param name="n" select="." />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="replace_sab">
<xsl:with-param name="s" select="." />
<xsl:with-param name="a" select="'
'" />
<xsl:with-param name="b"><br /></xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:template>
<xsl:template name="replace_sab">
<!-- with string s, replace substring a by string b -->
<!-- s, a and b are parameters determined upon calling -->
<xsl:param name="s" />
<xsl:param name="a" />
<xsl:param name="b" />
<xsl:choose>
<xsl:when test="contains($s,$a)">
<xsl:value-of select="substring-before($s,$a)" />
<xsl:copy-of select="$b" />
<xsl:call-template name="replace_sab">
<xsl:with-param name="s" select="substring-after($s,$a)" />
<xsl:with-param name="a" select="$a" />
<xsl:with-param name="b" select="$b" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>

for loop and use the value

I've the below line in XML.
<tb class="3">
<tr>
<td>
<b>English words </b>
</td>
<td>
<b>Arabic </b>
</td>
<td al="r">
<b>Arabic</b>
</td>
</tr>
<tr>
<td>bear </td>
<td>ḍam</td>
<td al="r">new</td>
</tr>
</tb>
Here is my xslt.
<xsl:template name="table" match="tb">
<table class="frame-all">
<xsl:call-template name="cols"/>
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template name="cols">
<xsl:variable name="numbr" select="number(./#class)"/>
<xsl:variable name="colcnt" select="format-number(100 div $numbr,'##.#')"/>
<colgroup>
<!-- I want the condition here-->
</colgroup>
</xsl:template>
This gives me output of 33.3. And I want to create 3 cols(the class attribute value). And for each col the name should be increment value. as below.
<col name="1" width="33.3"/>
<col name="2" width="33.3"/>
<col name="3" width="33.3"/>
please let me know, how can i get the above result.
Thanks
Here is my answer.
<xsl:template name="table" match="tb">
<table class="frame-all">
<xsl:call-template name="cols"/>
<!--<xsl:apply-templates/>-->
</table>
</xsl:template>
<xsl:template name="colgroup">
<xsl:param name="count" select="./#cls"/>
<xsl:param name="final" select="1"/>
<xsl:variable name="colcnt" select="format-number(100 div number(./#cls),'##.#')"/>
<col class="colnum-{$final} colname-col{$final} colwidth-{$colcnt}"></col>
<xsl:if test="$final < $count">
<xsl:call-template name="colgroup">
<xsl:with-param name="final" select="$final +1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="cols">
<xsl:variable name="numbr" select="number(./#cls)"/>
<xsl:variable name="colcnt" select="format-number(100 div $numbr,'##.#')"/>
<colgroup>
<xsl:call-template name="colgroup"/>
</colgroup>
<xsl:for-each select="tr">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="tr">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="td">
<td>
<xsl:attribute name="align">
<xsl:choose>
<xsl:when test="./#al='r'">
<xsl:text>right</xsl:text>
</xsl:when>
<xsl:when test="./#al='c'">
<xsl:text>center</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>left</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates/>
</td>
</xsl:template>
If you are using XSLT 2.0, then one way to generate a specific number of elements, in your case col elements, is to use a variation of the xsl:for-each to do a specific number of iterations.
So, instead of doing <xsl:call-template name="colgroup"/> to call a recursive template, you could do this:
<colgroup>
<xsl:for-each select="1 to xs:integer($numbr)">
<col name="{.}" width="{$colcnt}"/>
</xsl:for-each>
</colgroup>
Note that you will need to define the xs namespace prefix in your stylesheet as follows
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

apply- templates select substring after

I've the below XML.
<toc-item>
<toc-title>
6A. <content-style font-style="italic">(Repealed 64 of 1989 s.9)
</content-style>
</toc-title>
<toc-pg>U2/6A</toc-pg>
</toc-item>
and i'm running with the below XSLT.
<xsl:template name="toc" match="toc">
<div class="toc">
<div class="toc-part">
<table class="toc-div">
<tbody>
<tr>
<td>
<xsl:for-each select="toc-part/toc-div/toc-item">
<xsl:call-template name="toc-item"/>
</xsl:for-each>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</xsl:template>
<xsl:template name="toc-item" match="toc-item">
<xsl:variable name="tocpg">
<xsl:value-of select="concat('P',normalize-space(current()/toc-pg/text()))"/>
</xsl:variable>
<xsl:variable name="tocpgtag">
<xsl:choose>
<xsl:when test="contains($tocpg,'.')">
<xsl:value-of select="translate($tocpg,'.', '-')"/>
</xsl:when>
<xsl:when test="contains($tocpg,'/')">
<xsl:value-of select="translate($tocpg,'/', '-')"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="chapternumber">
<!-- Get num attribute of parent node -->
<xsl:value-of select="ancestor::chapter[1]/#num"/>
</xsl:variable>
<xsl:variable name="nu">
<xsl:number format="I."/>
</xsl:variable>
<xsl:variable name="Brac">
<xsl:call-template name="get_number_type">
<xsl:with-param name="number_string" select="./#num"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="NewL">
<xsl:value-of select="normalize-space($chapternumber)"/>
</xsl:variable>
<xsl:variable name="size">
<xsl:value-of select="fn:string-length($NewL)"/>
</xsl:variable>
<xsl:variable name="newNum">
<xsl:choose>
<xsl:when test="$size=1">
<xsl:value-of select="concat('0',normalize-space($NewL))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space($NewL)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="d">
<xsl:value-of select="concat('toc-item-',$ThisDocument//ntw:nums[#num=1]/#word,'-level')"/>
</xsl:variable>
<xsl:variable name="new">
<xsl:value-of select="concat('er:#HKWBV1_ORD_',$newNum,'/',$tocpgtag)"/>
</xsl:variable>
<table class="toc-item-first-level">
<tbody>
<tr>
<xsl:choose>
<xsl:when test="./toc-pg">
<xsl:choose>
<xsl:when test="fn:contains(./toc-title,'.')">
<td class="toc-item-num">
<xsl:value-of select="concat(substring-before(./toc-title,'.'),'.')"/>
</td>
</xsl:when>
<xsl:otherwise>
<td colspan="2" align="left" class="padtit">
<xsl:value-of select="./toc-title"/>
</td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="fn:contains(./toc-title,'.')">
<td class="toc-title">
<xsl:apply-templates select="./toc-title" mode="x"/>
</td>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
<td class="toc-pg">
<xsl:choose>
<xsl:when test="contains(./toc-pg,'.')">
<xsl:value-of select="normalize-space(current()/toc-pg)"/>
</xsl:when>
<xsl:when test="contains(./toc-pg,'/')">
<a href="{$new}">
<xsl:value-of select="normalize-space(current()/toc-pg)"/>
</a>
</xsl:when>
<xsl:otherwise>
<a href="{concat('#pg_',toc-pg)}">
<xsl:value-of select="normalize-space(current()/toc-pg)"/>
</a>
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:when>
<xsl:otherwise>
<td align="center" colspan="3">
<span class="font-style-bold">
<xsl:apply-templates select="./toc-title"/>
</span>
</td>
</xsl:otherwise>
</xsl:choose>
</tr>
</tbody>
</table>
<!--</table>-->
</xsl:template>
<xsl:template match="toc-title/text()" mode="x">
<xsl:analyze-string select="substring-after(.,'.')" regex="(\w)">
<xsl:matching-substring>
<xsl:apply-templates select="regex-group(1)"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
here what i'm trying to achieve is in my XML in toc-title, there is a number followed by content-style, here my output is having 2 <td>
the number before .
Content after .
if there is no other tag except text after ., i'm able to get the text correctly, and i want to apply-templates for the content after ., please let me know how i can get this done.
here is the demo
Thanks

XSLT Confusion with xsl:apply-templates

I have an XML file with this format:
<?xml version="1.0" encoding="utf-8" ?>
<OpacResult>
<valueObjects class="list">
<Catalog>
<notes>
Daily newsletter available via e-mail.
IP authenticated. Login not needed within firm.
</notes>
<title>Health law360. </title>
<url>http://health.law360.com/</url>
<catalogTitles class="list">
<CatalogTitle>
<uuid>e5e2bc53ac1001f808cddc29f93ecad8</uuid>
<timeChanged class="sql-timestamp">2010-12-14 09:17:10.707</timeChanged>
<timeEntered class="sql-timestamp">2010-12-14 09:17:10.707</timeEntered>
<whoChanged>B23DE2FFE8DD49B0B0A03D1FEB3E7DA2</whoChanged>
<whoEntered>B23DE2FFE8DD49B0B0A03D1FEB3E7DA2</whoEntered>
<updateSearchIndex>true</updateSearchIndex>
<corpId>RopesGray</corpId>
<catalogUuid>a20b6b4bac1001f86d28280ed0ebeb9e</catalogUuid>
<type>O</type>
<title>Law 360. Health law.</title>
</CatalogTitle>
<CatalogTitle>
<uuid>e5e2bc53ac1001f808cddc299ddfe49d</uuid>
<timeChanged class="sql-timestamp">2010-12-14 09:17:10.707</timeChanged>
<timeEntered class="sql-timestamp">2010-12-14 09:17:10.707</timeEntered>
<whoChanged>B23DE2FFE8DD49B0B0A03D1FEB3E7DA2</whoChanged>
<whoEntered>B23DE2FFE8DD49B0B0A03D1FEB3E7DA2</whoEntered>
<updateSearchIndex>true</updateSearchIndex>
<corpId>RopesGray</corpId>
<catalogUuid>a20b6b4bac1001f86d28280ed0ebeb9e</catalogUuid>
<type>O</type>
<title>Health law 360</title>
</CatalogTitle>
<CatalogTitle>
<uuid>e5e2bc53ac1001f808cddc29ec1d959b</uuid>
<timeChanged class="sql-timestamp">2010-12-14 09:17:10.707</timeChanged>
<timeEntered class="sql-timestamp">2010-12-14 09:17:10.707</timeEntered>
<whoChanged>B23DE2FFE8DD49B0B0A03D1FEB3E7DA2</whoChanged>
<whoEntered>B23DE2FFE8DD49B0B0A03D1FEB3E7DA2</whoEntered>
<updateSearchIndex>true</updateSearchIndex>
<corpId>RopesGray</corpId>
<catalogUuid>a20b6b4bac1001f86d28280ed0ebeb9e</catalogUuid>
<type>O</type>
<title>Health law three hundred sixty</title>
</CatalogTitle>
</catalogTitles>
<catalogUrls class="list"/>
<gmd>
<uuid>f8f123acc0a816070192e296a6a71715</uuid>
<timeChanged class="sql-timestamp">2006-10-10 15:23:37.813</timeChanged>
<timeEntered class="sql-timestamp">2005-01-27 00:00:00.0</timeEntered>
<whoChanged>25db9fcd3fd247f4a20485b40cc134ad</whoChanged>
<whoEntered>user</whoEntered>
<updateSearchIndex>true</updateSearchIndex>
<corpId>RopesGray</corpId>
<isRuleDefault>false</isRuleDefault>
<ruleName>text</ruleName>
<term>electronic resource</term>
<preferCollection>false</preferCollection>
<isTechnicalManual>false</isTechnicalManual>
<sip2IsMagnetic>false</sip2IsMagnetic>
</gmd>
<issues class="list"/>
</Catalog>
</valueObjects>
</OpacResult>
As you can see, there are other elements under sibling nodes, but I don't care about these and only want to see the first one.
I'm using this code to call a template with the string of desired elements as the parameter
and a template to loop through the asterisk-delimited string parameter: (title*url*notes*)
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="columns" />
<xsl:template match="/OpacResult/valueObjects">
<html>
<body>
<table border="1">
<!-- Header row -->
<tr>
<xsl:call-template name="print-headers">
<xsl:with-param name="columns" select="$columns"/>
</xsl:call-template>
</tr>
<!-- Value rows -->
<xsl:for-each select="Catalog">
<tr>
<xsl:call-template name="print-values">
<xsl:with-param name="columns" select="$columns"/>
</xsl:call-template>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<!-- Split up string of column names and create header field names based on element names-->
<xsl:template name="print-headers">
<xsl:param name="columns"/>
<xsl:variable name="newList" select="$columns"/>
<xsl:variable name="first" select="substring-before($newList, '*')" />
<xsl:variable name="remaining" select="substring-after($newList, '*')" />
<th>
<xsl:apply-templates select="Catalog/*[name()=$first]">
<xsl:with-param name="header">true</xsl:with-param>
</xsl:apply-templates>
</th>
<xsl:if test="$remaining">
<xsl:call-template name="print-headers">
<xsl:with-param name="columns" select="$remaining"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="print-values">
<xsl:param name="columns"/>
<xsl:variable name="newList" select="$columns"/>
<xsl:variable name="first" select="substring-before($newList, '*')" />
<xsl:variable name="remaining" select="substring-after($newList, '*')" />
<td>
<xsl:apply-templates select="Catalog/*[name()=$first]"/>
</td>
<xsl:if test="$remaining">
<xsl:call-template name="print-values">
<xsl:with-param name="columns" select="$remaining"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="title">
<xsl:param name="header"/>
<xsl:choose>
<xsl:when test="$header='true'">
<xsl:text>Title</xsl:text>
</xsl:when>
<xsl:otherwise>
<a>
<xsl:attribute name="href">
<xsl:value-of select="//*[name()='url']"/>
</xsl:attribute>
<xsl:value-of select="//*[name()='title']"/>
</a>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="url">
<xsl:param name="header"/>
<xsl:choose>
<xsl:when test="$header='true'">
<xsl:text>URL</xsl:text>
</xsl:when>
<xsl:otherwise>
<a>
<xsl:attribute name="href">
<xsl:value-of select="//*[name()='url']"/>
</xsl:attribute>
<xsl:value-of select="//*[name()='url']"/>
</a>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="notes">
<xsl:param name="header"/>
<xsl:choose>
<xsl:when test="$header='true'">
<xsl:text>Notes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//*[name()='notes']"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="holdingNotes">
<xsl:param name="header"/>
<xsl:choose>
<xsl:when test="$header='true'">
<xsl:text>Holding Notes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//*[name()='holdingNotes']"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="relatedUrl">
<xsl:param name="header"/>
<xsl:choose>
<xsl:when test="$header='true'">
<xsl:text>Related URL</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//*[name()='relatedUrl']"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="bibliographicType/hasDataFile">
<xsl:param name="header"/>
<xsl:choose>
<xsl:when test="$header='true'">
<xsl:text>File</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="Catalog/*[name()='hasDataFile']"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The only way I can access this template is to use the //*[name()=$first] syntax to extract the value of the element based on the name from the $first parameter.
Any help is greatly appreciated. Thanks very much in advance. Not including the full XML as there are thousands of lines of unnecessary text.
This stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:h="header"
exclude-result-prefixes="h">
<h:h>
<title>Title</title>
<url>URL</url>
<notes>Notes</notes>
</h:h>
<xsl:param name="pColumns" select="'title url notes'"/>
<xsl:template match="/OpacResult/valueObjects">
<html>
<body>
<table border="1">
<tr>
<xsl:apply-templates
select="document('')/*/h:h"
mode="filter"/>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Catalog">
<tr>
<xsl:call-template name="filter"/>
</tr>
</xsl:template>
<xsl:template match="h:h/*">
<th>
<xsl:value-of select="."/>
</th>
</xsl:template>
<xsl:template match="Catalog/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
<xsl:template match="node()" mode="filter" name="filter">
<xsl:apply-templates select="*[contains(
concat(' ',$pColumns,' '),
concat(' ',name(),' '))]">
<xsl:sort select="substring-before(
concat(' ',$pColumns,' '),
concat(' ',name(),' '))"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
Output:
<html>
<body>
<table border="1">
<tr>
<th>Title</th>
<th>URL</th>
<th>Notes</th>
</tr>
<tr>
<td>Health law360. </td>
<td>http://health.law360.com/</td>
<td> Daily newsletter available via e-mail.
IP authenticated. Login not needed within firm. </td>
</tr>
</table>
</body>
</html>
Note: Inline data for headers, pseudo sequence parameter for filtering and sorting, modes not for processing the same element in different way but for processing different elements in the same way also.
I've found a solution, but I'm sure it's not the best way to do it. Within the templates for each of my expected fields, I have added:
<xsl:if test=position()=1">
.. process data here ..
</xsl:if>
Ideally, there would be a way to tell this to process only the first element it finds:
<th>
<xsl:apply-templates select="//*[name()=$first]">
<xsl:with-param name="header">true</xsl:with-param>
</xsl:apply-templates>
</th>
Edit: As I suspected, this will not work when there is more than one Catalog element to parse. So instead of grabbing the first element for each catalog parent element, it's grabbing the first element in the document every time

XSLT - Adding Elements dynamically

I've a need to display certain XML content in tabular form (XSL-FO for pdf reports),
and not all the columns to displayed are present in source XML. So, I was wondering
if there is a way to transform source XML by embedding additional columns based on
certain element values, and then process the resulting XML to display content?
As an example, for source data:
<projectteam>
<member>
<name>John Doe</name>
<role>dev</role>
<hrs>100</hrs>
</member>
<member>
<name>Paul Coder</name>
<role>dev</role>
<hrs>40</hrs>
</member>
<member>
<name>Henry Tester</name>
<role>qa</role>
<hrs>80</hrs>
</member>
<member>
<name>Peter Tester</name>
<role>qa</role>
<hrs>40</hrs>
</member>
</projectteam>
I'd like the data to be displayed as :
Name Role Dev QA
---------------------------
John Doe dev 100
Paul Coder dev 40
Henry Tester qa 80
Peter Tester qa 40
---------------------------
Role Totals: 140 120
---------------------------
I would like to know if I can use something like:
<xsl:element name="{role}">
<xsl:value-of select="member/hrs"/>
</xsl:element>
So that I can embed elements <dev>100</dev> and so on at run time during
first pass, and then use the resulting XML to display data for new columsn 'dev'
and 'qa', that way, calculating totals for each role type will be much simpler
(for eg. "sum(preceding-sibling::member/dev)" for dev column), and the data for each
cell in 'dev' and 'qa' colums could simply be the value-of these tags respectively.
It got the desired results the hard way using following stylesheet (page formatting
details omitted to keep it brief), but am not convinced that this is the apt solution.
...
<fo:table-body>
<!-- fills table rows -->
<xsl:apply-templates select="member"/>
<!-- dislpay totals for each role -->
<fo:table-row height="12pt" border-bottom="1pt solid black">
<fo:table-cell number-columns-spanned="2">
<fo:block>Role Totals:</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right">
<xsl:call-template name="RoleTotals">
<xsl:with-param name="node" select="//member[1]"/>
<xsl:with-param name="roleName" select="'dev'"/>
</xsl:call-template>
</fo:table-cell>
<fo:table-cell text-align="right">
<xsl:call-template name="RoleTotals">
<xsl:with-param name="node" select="//member[1]"/>
<xsl:with-param name="roleName" select="'qa'"/>
</xsl:call-template>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
...
</fo:root>
</xsl:template>
<xsl:template match="member">
<fo:table-row border-bottom="1pt solid black">
<fo:table-cell> <fo:block> <xsl:value-of select="name"/></fo:block></fo:table-cell>
<fo:table-cell> <fo:block> <xsl:value-of select="role"/></fo:block></fo:table-cell>
<fo:table-cell text-align="right">
<fo:block>
<xsl:if test="role = 'dev'"><xsl:value-of select="hrs"/></xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right">
<fo:block>
<xsl:if test="role = 'qa'"><xsl:value-of select="hrs"/></xsl:if>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template name="RoleTotals">
<xsl:param name="node"/>
<xsl:param name="roleName"/>
<xsl:param name="RT" select="0"/>
<xsl:variable name="newRT">
<xsl:choose>
<xsl:when test="$node/role = $roleName">
<xsl:value-of select="$RT + $node/hrs"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$RT"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$node/following-sibling::member">
<xsl:call-template name="RoleTotals">
<xsl:with-param name="node" select="$node/following-sibling::member[1]"/>
<xsl:with-param name="roleName" select="$roleName"/>
<xsl:with-param name="RT" select="$newRT"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<fo:block><xsl:value-of select="$newRT"/></fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
What would happen if more job roles, other than Dev and QA got added? Would your stylesheet be able to cope? Maybe you can make use of Muenchian Grouping to get all possible roles in the stylesheet, and then generate columns for each possible role dynamically?
<xsl:key name="roles" match="role" use="."/>
<xsl:template match="/projectteam">
<table border="1">
<tr>
<td>Name</td>
<td>Role</td>
<xsl:for-each select="member[generate-id(role) = generate-id(key('roles', role)[1])]">
<td>
<xsl:value-of select="role"/>
</td>
</xsl:for-each>
</tr>
<xsl:apply-templates select="member"/>
</table>
</xsl:template>
<xsl:template match="member">
<xsl:variable name="currentrole" select="role"/>
<xsl:variable name="currenthrs" select="hrs"/>
<tr>
<td>
<xsl:value-of select="name"/>
</td>
<td>
<xsl:value-of select="role"/>
</td>
<xsl:for-each select="/projectteam/member[generate-id(role) = generate-id(key('roles', role)[1])]">
<td>
<xsl:choose>
<xsl:when test="$currentrole = role">
<xsl:value-of select="$currenthrs"/>
</xsl:when>
</xsl:choose>
</td>
</xsl:for-each>
</tr>
</xsl:template>
I've outputted as HTML, not XSL-FO, but maybe this gives you the general idea.
Yes, you can.
To answer your question. I haven't read the huge stylesheet, to be true.