Create a attribute dynamically - xslt

I'm writing an xsl script in which I need to create the attribute dynamically.
Here is my sample code.
<Table BORDERLINESTYLE="solid" BOTTOMMARGIN="12" NOTEGROUPSPACING="single" CELLPADDING="0" CELLSPACING="0" WIDTH="504"></Table>
Here BORDERLINESTYLE may or may not be available to all Table tags. and I want the output to be something like this.
<table style="border-style:solid; border-margin:12px; width:504px"></table>
Here are 2 things that I've tried.
1. Creating attributes
<table>
<xsl:if test="./#BORDERLINESTYLE">
<xsl:attribute name="border" select="'1px solid'"/>
</xsl:if>
<xsl:if test="./#WIDTH">
<xsl:attribute name="width" select="concat(./#WIDTH, 'px')"/>
</xsl:if>
<xsl:if test="./#BOTTOMMARGIN">
<xsl:attribute name="border" select="'1px solid'"/>
</xsl:if>
<xsl:apply-templates/>
</table>
the output that I get is as below.
<table border="1px solid" width="504px">
2. Inline adding
<table style="width:{current()/#WIDTH,'px'}; border:{./#BORDERLINESTYLE}; margin-bottom: {./#BOTTOMMARGIN, 'px'}; margin-top:{./#TOPMARGIN, 'px'}; "></table>
and the output is as below with some blank values like margin-top
<table style="width:504 px; border:solid; margin-bottom: 12 px; margin-top:px; ">
How can I add styles to style based on the attributes provided in the XML?
I'm using XSLT2.0.

I would use something like this:
<xsl:if test="#BORDERLINESTYLE|#WIDTH|#BOTTOMMARGIN|#TOPMARGIN">
<xsl:attribute name="style">
<xsl:if test="#BORDERLINESTYLE">
<xsl:text>border:1px solid;</xsl:text>
</xsl:if>
<xsl:if test="#WIDTH">
<xsl:value-of select="concat('width:',#WIDTH, 'px;')"/>
</xsl:if>
<xsl:if test="#BOTTOMMARGIN">
<xsl:value-of select="concat('margin-bottom:',#BOTTOMMARGIN, 'px;')"/>
</xsl:if>
<xsl:if test="#TOPMARGIN">
<xsl:value-of select="concat('margin-top:',#TOPMARGIN, 'px;')"/>
</xsl:if>
</xsl:attribute>
</xsl:if>
And depending on the business rules for mapping those source-attributes to style attributes change the code accordingly.
Btw.: ./# is the same as #

Use if else Function
For Example
margin-top: {if(//#TOPMARGIN!=0) then {./#TOPMARGIN, 'px'} else '12px'}

I need to create the attribute dynamically.
Actually, you want to populate the attribute dynamically - which could be done by applying a template to each input attribute you want to use:
<xsl:template match="Table">
<table>
<xsl:attribute name="style">
<xsl:apply-templates select="#BORDERLINESTYLE | #BOTTOMMARGIN | #WIDTH"/>
</xsl:attribute>
</table>
</xsl:template>
<xsl:template match="#BORDERLINESTYLE">
<xsl:text>border-style:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>; </xsl:text>
</xsl:template>
<xsl:template match="#BOTTOMMARGIN">
<xsl:text>border-margin:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>px; </xsl:text>
</xsl:template>
<xsl:template match="#WIDTH">
<xsl:text>width:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>; </xsl:text>
</xsl:template>

Related

Add link in author label - DSpace 6.2

I've trying to edit item-view.xsl to add a link in author label (dc.contributor.author) but no luck yet.
I'm using XMLUI - Mirage2
Author link example image
in: https://openknowledge.worldbank.org/handle/10986/29498
What should I add?
Thanks
The following code snippet might help. This code turns the subject field into a facet link. It will require a little modification to work for the author field.
<xsl:variable name="H_SUBJECT">Subject</xsl:variable>
<xsl:variable name="DFILTER_SUBJECT">/discover?filtertype=subject&filter_relational_operator=equals&filter=</xsl:variable>
<xsl:template name="itemSummaryView-DIM-subject">
<xsl:if test="dim:field[#element='subject']">
<div class="simple-item-view-description item-page-field-wrapper table">
<h5><xsl:value-of select="$H_SUBJECT"/></h5>
<div>
<xsl:for-each select="dim:field[#element='subject']">
<xsl:choose>
<xsl:when test="node()">
<a class="gu-subject-link" href="{concat($FILTER_SUBJECT,.)}">
<span>
<xsl:apply-templates select="." mode="microtag-prop"/>
<xsl:apply-templates select="text()"/>
</span>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="count(following-sibling::dim:field[#element='subject']) != 0">
<xsl:text>; </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:if test="count(dim:field[#element='subject']) > 1">
<xsl:text>; </xsl:text>
</xsl:if>
</div>
</div>
</xsl:if>
</xsl:template>

How to implement attribute specific templates without redundancy

I'm familiar with using templates that key off attributes as follows (e.g. key off of the presence of foo):
<xsl:template match="something[#foo]">
<xsl:template match="something[not(#foo)]">
However, if much of the content of these templates is the same, is there a better way that still uses templates, since the community appears to prefer them? Or is the solution to just use xsl:choose. It's clearly preferable not to write duplicate code that must be maintained in both templates.
EDIT:
Here's my specific set of templates:
<xsl:template match="item[not(#format)]">
<td class="{current()/../#name}_col status_all_col">
<xsl:value-of select="current()"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
<xsl:template match="item[#format]">
<td class="{current()/../#name}_col status_all_col">
<xsl:value-of select="format-number(current(), #format)"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
and here's what I currently have using choose:
<xsl:template match="item">
<td class="{current()/../#name}_col status_all_col">
<xsl:choose>
<xsl:when test="#format">
<xsl:value-of select="format-number(current(), #format)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="current()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
I would definitely use xsl:choose in this situation.
Just to demonstrate how you could use templates without code repetition:
<xsl:template match="item">
<td class="{../#name}_col status_all_col">
<xsl:apply-templates select="." mode="format"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
<xsl:template match="item[not(#format)]" mode="format">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="item[#format]" mode="format">
<xsl:value-of select="format-number(., #format)"/>
</xsl:template>
But I fail to see what advantage this would have. On the contrary, it suffers from the GOTO syndrome.
BTW, this may not be the best example, since I believe that supplying an empty string as the second argument of the format-number() function will result in the number being returned as-is.
So you could just use a single template to match all:
<xsl:template match="item">
<td class="{../#name}_col status_all_col">
<xsl:value-of select="format-number(current(), #format)"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>

XSLT setting a variable to either foo or #foo depending on presence of attribute

I'm trying to set parttext to #head if use_head="true" is not present and to the contents of <head> </head> if use_head="true" is present
<xsl:template match="toc_part">
<xsl:variable name="artnum">
<xsl:value-of select="../#num" />
</xsl:variable>
<xsl:variable name="partnum">
<xsl:value-of select="#num" />
</xsl:variable>
<xsl:variable name="parttext">
<xsl:if test="#use_head">
<xsl:value-of select="head"/>
</xsl:if>
<xsl:if test="not[#use_head]">
<xsl:value-of select="#head"/>
</xsl:if>
</xsl:variable>
<tr>
<td>
 
</td>
<td>
<a class="int" href="{$GBLfilePre}{$artnum}.html#{$artnum}{$partnum}"><xsl:text>Part </xsl:text><xsl:value-of select="$partnum" /></a>
</td>
<td>
<xsl:value-of select="$parttext" />
</td>
</tr>
</xsl:template>
I've also tried:
<xsl:if test="#use_head"><xsl:variable name="parttext"><xsl:value-of select="head"></xsl:variable>
<xsl:if test="not[#use_head"]><xsl:variable name="parttext"><xsl:value-of select="#head"></xsl:variable>
which give parttext undefined when referenced.
Try:
<xsl:variable name="parttext">
<xsl:choose>
<xsl:when test="#use_head='true'">
<xsl:value-of select="head"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#head"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Your current test in the first if statement will evaluate to true() if the #use_head attribute exists, regardless of it's value. If you want the test to evaluate whether or not it's value is equal to "true" you should use test="#use_head='true'".
The second if statement is evaluating whether or not there is an element named "not" that has an attribute called "use_head", because the square brackets are a predicate. I believe you had intended to use the not() function: not(#use_head='true')
You could then use an <xsl:choose> instead of two if blocks:
<xsl:variable name="parttext">
<xsl:choose>
<xsl:when test="#use_head='true'">
<xsl:value-of select="head"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#head"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
In XSLT 2.0:
<xsl:variable name="head"
select="if (#use_head='true') then head else #head"/>
In XSLT 1.0, either Hansen's solution, or
<xsl:variable name="head"
select="head[current()/#use_head='true'] |
#head[not(current()/#use_head='true')]"/>
In future, please tell us which version of XSLT you are using. One can sort of guess this must be XSLT 1.0 because in 2.0 the solution is trivial, but it's better not to have to guess.

how to use parameter in xsl?

I have a simple xsl file:
<xsl:for-each select="part">
<div class="part">
<h2>Part Name:<xsl:value-of select="#partName" /></h2>
<xsl:for-each select="input">
<p>
<input>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
<xsl:attribute name="name"><xsl:value-of select="#inputName" /></xsl:attribute>
</input>
</p>
</xsl:for-each>
</div>
</xsl:for-each>
I want to save partname and use it latter. I tried as the following:
<xsl:for-each select="part">
<div class="part">
<h2>Part Name:<xsl:value-of select="#partName" /></h2>
**<xsl:param name="part_name"><xsl:value-of select="#partName" /></xsl:param>**
<xsl:for-each select="input">
<p>
<input>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
<xsl:attribute name="name">**<xsl:value-of select="$part_name" />**-<xsl:value-of select="#inputName" /></xsl:attribute>
</input>
</p>
</xsl:for-each>
</div>
</xsl:for-each>
but it doesn't work. I don't know what should do.
You are probably getting the error Keyword xsl:param may not be used here.. This is because xsl:param should only really be found under the root xsl:stylesheet element, and are used for passing values from the external application.
What you need is xsl:variable in this case
<xsl:variable name="part_name">
<xsl:value-of select="#partName"/>
</xsl:variable>
Or simpler still....
<xsl:variable name="part_name" select="#partName"/>
In fact, you could do away with the variable altogether. Instead of doing this
<xsl:value-of select="$part_name"/>
You could just do this, to get the value from the parent element
<xsl:value-of select="../#part_name"/>
Do bear in mind, if you do use a variable, it will only be local to the scope of the xsl:for-each in this case.

apply xslt to xml content

I am using xslt. My input file is
<table bnf_id="bnf_204272" colsep="1" frame="all" id="nzf_3865" otherprops="HRT Risk table" pgwide="1" rowsep="1"><title>HRT Risk</title><tgroup cols="8" colsep="1" rowsep="1"><colspec colname="col1" /><colspec colname="col2" /><thead><row><entry morerows="1" rowsep="1">Risk</entry><entry morerows="1" rowsep="1">Age range (years)</entry><entry colsep="1" nameend="col4" namest="col3" rowsep="1">Background incidence per 1000 women in Europe not using HRT</entry><entry colsep="1" nameend="col6" namest="col5" rowsep="1"><b>Additional</b>
cases per 1000 women using
<b>oestrogen only HRT</b>
(estimated)
</entry><entry colsep="1" nameend="col8" namest="col7" rowsep="1"><b>Additional</b>
cases per 1000 women using
<b>combined (oestrogen-progestogen) HRT</b>
(estimated)
</entry></row><row><entry>Over 5 years</entry><entry nameend="col8" namest="col1"><b>Note</b><p>Where background incidence or additional cases have not been included in the table, this indicates a lack of available data. NS indicates a non-significant difference</p><p>
Taken from MHRA/CHM (<i>Drug Safety Update</i>
2007;
<b>1</b>
(2): 2–6) available at
<xref format="html" href="http://www.mhra.gov.uk/drugsafetyupdate">
www.mhra.gov.uk/drugsafetyupdate
</xref></p></entry></row></tbody></tgroup></table>
My xslt is
<xsl:template match="tgroup">
<tr>
<xsl:if test="#cols">
<xsl:attribute name="cols">
<xsl:value-of select="#cols"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#colsep">
<xsl:attribute name="colsep">
<xsl:value-of select="#colsep"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#rowsep">
<xsl:attribute name="rowsep">
<xsl:value-of select="#rowsep"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#align">
<xsl:attribute name="align">
<xsl:value-of select="#align"/>
</xsl:attribute>
</xsl:if>
<xsl:for-each select="colspec">
<th>
<xsl:if test="#colwidth">
<xsl:attribute name="width">
<xsl:value-of select="#colwidth"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#colname">
<xsl:attribute name="colname">
<xsl:value-of select="#colname"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#align">
<xsl:attribute name="align">
<xsl:value-of select="#align"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#colsep">
<xsl:attribute name="colsep">
<xsl:value-of select="#colsep"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="#colnum">
<xsl:attribute name="colnum">
<xsl:value-of select="#colnum"/>
</xsl:attribute>
</xsl:if>
</th>
</xsl:for-each>
</tr>
<xsl:apply-templates/>
But having the above code in same XSLT file, my second match for th is executed.
So i get the empty th like
<th></th>
But i need to insert the ... between the tags. How to solve it?
Couldn't you simply add the following xsl:if condition before the closing </th> tag?
<xsl:if test="#colnum">
<xsl:attribute name="colnum">
<xsl:value-of select="#colnum"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="text() = ''">
<xsl:text>...</xsl:text>
</xsl:if>
</th>