In the work I do I seem to see a lot of code liek this..
<xsl:choose>
<xsl:when test="long_xpath_to_optional/#value1">
<xsl:value-of select="long_xpath_to_optional/#value"/>
</xsl:when>
<xsl:when test="another_long_xpath_to_optional/#value">
<xsl:value-of select="another_long_xpath_to_optional/#value"/>
</xsl:when>
<etc>
</etc>
<otherwise>
<xsl:value-of select="default_long_xpath_to_value"/>
</otherwise>
</xsl:choose>
its very long and very repetitive.
When I'm were working in some other (psuedo) language I would go
let values = concat(list(long_xpath_to_optional_value),list(another_long_xpath_to_optional_value))
let answer = tryhead(values,default_long_xpath_to_value)
i.e. create a list of values in priority order, and then take the head.
I only evaluate each path once
how would you do something similar in XSLT 1.0 (we can use node-sets).
I was wondering if you can create a node-set somehow
You can - but it's not going to be any shorter:
<xsl:variable name="values">
<xsl:apply-templates select="long_xpath_to_optional/#value" mode="values"/>
<xsl:apply-templates select="another_long_xpath_to_optional/#value" mode="values"/>
<xsl:apply-templates select="default_long_xpath_to_value/#value" mode="values"/>
</xsl:variable>
<xsl:value-of select="exsl:node-set($values)/value[1]" xmlns:exsl="http://exslt.org/common"/>
and then:
<xsl:template match="#value" mode="values">
<value>
<xsl:value-of select="."/>
</value>
</xsl:template>
But at least the repetition is eliminated.
Alternatively, you could do:
<xsl:template match="#value" mode="values">
<xsl:value-of select="."/>
<xsl:text>|</xsl:text>
</xsl:template>
and then:
<xsl:value-of select="substring-before($values, '|')"/>
To use variables you write
<xsl:variable name="value1" select="long_xpath_to_optional/#value1"/>
<xsl:variable name="value2" select="another_long_xpath_to_optional/#value"/>
<xsl:variable name="value3" select="default_long_xpath_to_value"/>
and then in XPath 2 or 3 all you would need is ($value1, $value2, $value3)[1] or head(($value1, $value2, $value3)) but in XSLT 1 with XPath 1 all you can write as a single expression is ($value1 | $value2 | $value3)[1] which sorts in document order so unless the document order is the same as your test order this wouldn't work to check the values; rather you would need to maintain the
<xsl:choose>
<xsl:when test="$value1">
<xsl:value-of select="$value1"/>
</xsl:when>
<xsl:when test="$value2">
<xsl:value-of select="$value2"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value3"/>
</xsl:otherwise>
</xsl:choose>
Of course in XPath 2 you wouldn't really need the variables and could use (long_xpath_to_optional/#value1, another_long_xpath_to_optional/#value, default_long_xpath_to_value)[1] as well directly.
I am having an issue that I cannot figure out in XLST where there are hardcoded 0's being added to the end of a string that I am not calling for. I am using a choose element to prompt placement of the string or to otherwise pick three 0's.
Can anyone tell in my code what I am doing wrong? See below:
<xsl:for-each select="Export/Record">
<xsl:if test="DebitAmount!=0 and DebitAmount!=''">
<xsl:value-of select="ChargedCorpLedgerCode" /><xsl:text>,</xsl:text>
<xsl:value-of select="DepartmentLedgerCode" /><xsl:text>,</xsl:text>
<xsl:value-of select="CategoryJournalNumber" /><xsl:text>,</xsl:text>
<xsl:value-of select="PFAM" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="LOC" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="ACTV" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="CLIENT"/><xsl:text> 0000000,</xsl:text>
<xsl:choose>
<xsl:when test="ProjectLedgerCode=null">
<xsl:value-of select="ProjectLedgerCode" /><xsl:text>,</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="ProjectLedgerCode" /><xsl:text> 000,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="DebitAmount" /><xsl:text>,</xsl:text>
<xsl:value-of select="''" /><xsl:text>,</xsl:text>
<xsl:value-of select="CategoryDesc" /><xsl:text>,</xsl:text>
<xsl:text>
</xsl:text>
</xsl:if>
my outcome looks like the below where the 000's are adding correctly when the column is blank, but when it is not, it adds the ProjectLedgerCode + 000
This test:
<xsl:when test="ProjectLedgerCode=null">
will return true if the string-value of ProjectLedgerCode is equal to the string-value of a sibling element named null.
If you want to test for ProjectLedgerCode not having a string-value, use:
<xsl:when test="ProjectLedgerCode=''">
or:
<xsl:when test="not(string(ProjectLedgerCode))">
In addition, I believe your results are mixed up.
I have been researching and haven't been able to find anything related to optimizing XSLT. Below is the snippet that I am working on and wanted to see if anything can be done to help with the xslt transformation.
<xsl:template match="a:OBR/*">
<xsl:choose>
<xsl:when test ="name() = 'OBR-10' and string-length(.) = 0">
<OBR-10>USER</OBR-10>
</xsl:when>
<xsl:when test ="name() = 'OBR-18'">
<OBR-18>
<xsl:value-of select ="//a:PV1/a:PV1-44"/>
</OBR-18>
</xsl:when>
<xsl:when test ="name() = 'OBR-19'">
<OBR-19>
<xsl:if test = "string-length(str:tokenize(../a:OBR-18,'^')[5]) > 0">
<xsl:value-of select ="str:tokenize(../a:OBR-18,'^')[5]"/>
</xsl:if>
</OBR-19>
</xsl:when>
<xsl:when test ="name() = 'OBR-33'">
<OBR-33>
<xsl:value-of select ="translate(../parent::a:ORC[1]/a:ORC-4,'^','~')"/>
</OBR-33>
</xsl:when>
<xsl:when test="name()='NTE'">
<NTE>
<xsl:apply-templates/>
</NTE>
</xsl:when>
<xsl:when test="name()='DG1'"/>
<!--<DG1>
<xsl:apply-templates/>
</DG1>
</xsl:when>-->
<xsl:when test="name()='OBX'">
<OBX>
<xsl:apply-templates/>
</OBX>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I would suggest to write code like
<xsl:template match="a:OBR/*">
<xsl:choose>
<xsl:when test ="name() = 'OBR-10' and string-length(.) = 0">
<OBR-10>USER</OBR-10>
</xsl:when>
as
<xsl:template match="a:OBR/OBR-10[string-length() = 0]">
<xsl:copy>USER</xsl:copy>
</xsl:template>
or perhaps
<xsl:template match="a:OBR/OBR-10[. = '']">
<xsl:copy>USER</xsl:copy>
</xsl:template>
that is, to write templates that match each element by its name and if needed a predicate/condition instead of that odd approach to match on * and then test the name. I don't see that necessarily as an optimization (you would have to measure with a particular implementation) but as a clear and modular coding style.
The
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
would then be code as
<xsl:template match="a:OBR/*">
<xsl:copy-of select="."/>
</xsl:template>
or probably already covered by an identity transformation template set up as the starting point to initiate and keep up the processing.
You would have to show the namespaces in the input document and the XSLT to allow a precise suggestion in terms of namespaces (could be that you want/need xsl:template match="a:OBR/a:OBR-10[string-length() = 0]").
I'm trying to make a variable, which tells me, whether node1 has something in it or not. node1 can be an empty element or it may contain an attribute. But right now I would like to know how achieve answer "false" when the node1 is empty .
<xsl:variable name="elementHasData">
<xsl:choose>
<xsl:when test="node1 != ''">
<xsl:value-of select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Check whether node1 contains text or other child nodes:
<xsl:variable name="elementHasData" select="node1/node()" />
Check whether node1 contains child nodes or attributes:
<xsl:variable name="elementHasData" select="node1/node() or node1/#*" />
You can try this in XSLT 2.0:
<xsl:variable name="elementHasData" select="if (node1[node()]) then 'true()' else 'false()'"/>
<xsl:variable name="elementHasData">
<xsl:choose>
<xsl:when test="string-length(normalize-space(node1)) gt 0">
<xsl:value-of select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
If you need to check either any node or any attribute then you can go to this.
<xsl:variable name="elementHasData" select="if (node1[#* | node()]) then 'true()' else 'false()'"/>
Hi i've the below xslt for creating anchor tags.
<xsl:template match="para/text()">
<xsl:variable name="numx">
<xsl:number format="1" level="any"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="(contains(substring(substring-after(current(),'.'),4,1),')') or contains(substring(substring-after(current(),'.'),4,1),'.') or contains(substring(substring-after(current(),'.'),4,1),' ')) and (contains(substring (current(),string-length(substring-before(current(), '.')) -1,2),' ')) and contains(substring(current(),string-length(substring-before(current(), '.')) -2,1),$numx)">
<xsl:variable name="before">
<xsl:value-of select="normalize-space(substring(current(),string-length(substring-before(current(), '.')) -1,2))"/>
</xsl:variable>
<xsl:variable name="NewN">
<xsl:value-of select="concat('0',$before)"/>
</xsl:variable>
<xsl:variable name="after">
<xsl:value-of select="substring(substring-after(current(),'.'),1,3)"/>
</xsl:variable>
<xsl:variable name="befdNumb">
<xsl:value-of select="substring-before(current(),$before)"/>
</xsl:variable>
<xsl:variable name="aftdNumb">
<xsl:value-of select="substring-after(current(),$after)"></xsl:value-of>
</xsl:variable>
<xsl:value-of select="$befdNumb"/>
<xsl:text> </xsl:text>
<a href="{concat('er:#CLI_CH_',$NewN,'/','P',normalize-space($before),'-',$after)}">
<xsl:value-of select="concat(normalize-space($before),'.',$after)"/>
</a>
<xsl:value-of select="$aftdNumb"/>
</xsl:when>
<xsl:when test="(contains(substring(substring-after(current(),'.'),4,1),')') or contains(substring(substring-after(current(),'.'),4,1),'.') or contains(substring(substring-after(current(),'.'),4,1),' ')) and contains(substring (current(),string-length(substring-before(current(), '.')) -2,1),' ')">
<xsl:variable name="before">
<xsl:value-of select="substring(current(),string-length(substring-before(current(), '.')) -2,3)"/>
</xsl:variable>
<xsl:variable name="NewN">
<xsl:value-of select="$before"/>
</xsl:variable>
<xsl:variable name="after">
<xsl:value-of select="substring(substring-after(current(),'.'),1,3)"/>
</xsl:variable>
<xsl:variable name="befdNumb">
<xsl:value-of select="substring-before(current(),$before)"/>
</xsl:variable>
<xsl:variable name="aftdNumb">
<xsl:value-of select="substring-after(current(),$after)"></xsl:value-of>
</xsl:variable>
<xsl:value-of select="$befdNumb"/>
<xsl:text> </xsl:text>
<a href="{concat('er:#CLI_CH_',$NewN,'/','P',normalize-space($before),'-',$after)}">
<xsl:value-of select="concat(normalize-space($before),'.',$after)"/>
</a>
<xsl:value-of select="$aftdNumb"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
but this xslt is getting applied i.e. the anchor tag is getting created if the text is like below.(having only '.' in the entire text).
serving on the company, by one of the means discussed at paragraph 12.012 below, a demand signed by the creditor
but i want it o be applied for the below text also
<para>the major issues here concern the notion of principal and ancillary jurisdictions, the ideal being that the ancillary jurisdiction will defer to the principal jurisdiction on most important matters, with a view to bringing about a just, practical and economically rational winding-up of affairs. A relatively recent development in connection with that ideal concerns the judicial promotion of court-endorsed agreements known as "crossborder protocols" between liquidators and similar officers appointed in different jurisdictions. It is important in this context, however, not to lose sight of the fact that certain matters of "administration" always remain governed by Hong Kong law. See paragraphs 12.016 to 12.032 below.</para>
please let me know how do i do it. i need to convert this number as er:#CLI_CH_12/P12-016 and er:#CLI_CH_12/P12-032
Thanks
I'm quite sure there could be a better way dot this, but because it is not entirely clear what this xslt should do I stay with your solution.
You need to do some recursive template calls.
Change your current "para/text()" template to a named template with text as an parameter.
But replace every current() with $text
<xsl:template name="mytext">
<xsl:param name="text" />
<xsl:variable name="numx">
<xsl:number format="1" level="any"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="(contains(substring(substring-after($text,'.'),4,1),')')
or contains(substring(substring-after($text,'.'),4,1),'.')
or contains(substring(substring-after($text,'.'),4,1),' '))
and (contains(substring ($text,string-length(substring-before($text, '.')) -1,2),' '))
and contains(substring($text,string-length(substring-before($text, '.')) -2,1),$numx)">
....
</xsl:template>
Add a new template to call the named one with the current text().
<xsl:template match="para/text()">
<xsl:call-template name="mytext" >
<xsl:with-param name="text" select="." />
</xsl:call-template>
</xsl:template>
Add an when just before the otherwise to output the text before a not handled dot and call the named template with text behind this dot.
<xsl:when test="contains(substring-after($text,'.'),'.')">
<xsl:value-of select="substring-before($text,'.')"/>
<xsl:text>.</xsl:text>
<xsl:call-template name="mytext">
<xsl:with-param name="text" select="substring-after($text,'.')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>