regex pattern match in XSLT - regex

I've the below XSL template
<xsl:template match="para">
<xsl:analyze-string select="." regex="\d+(?=[-, \d]*$)">
<xsl:matching-substring>
<xsl:variable name="prent">
<xsl:for-each select="document('C:\Users\u0138039\Desktop\Proview\SG\Commentary_SG_XML-03032014\SG-Singapore Precedents of Pleadings\title.xml')/entry/file">
<xsl:value-of select="normalize-space(document(concat('C:\Users\u0138039\Desktop\Proview\SG\Commentary_SG_XML-03032014\SG-Singapore Precedents of Pleadings\',./#name))//chapter/title[//page/#num=regex-group(1)])"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cha">
<xsl:value-of select="substring-before(substring-after($prent,'CHAPTER '),' ')"/>
</xsl:variable>
<xsl:variable name="size">
<xsl:value-of select="string-length($cha)"/>
</xsl:variable>
<xsl:variable name="conct">
<xsl:choose>
<xsl:when test="$size>'1'">
<xsl:value-of select="concat('er:#SPPR_CH_',$cha,'/pg_',regex-group(1))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('er:#SPPR_CH_0',$cha,'/pg_',regex-group(1))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<a href="{$conct}">
<xsl:value-of select="regex-group(1)"/>
</a>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
and below XML
<para>this is 1989 representing text 245</para>
<para>this is sample text 235</para>
<para>this is 234 sample text with comma seperator 345,756</para>
when i run this it is throwing me an error as below.
Invalid regular expression '\d+(?=[-, \d]*$)' - Details: - XTDE1140: The effective value of the 'regex' attribute of the <xsl:analyze-string> instruction must conform to the required syntax for regular expressions
here what i'm trying to achieve is, to get the number at the end of the text, ignoring any other characters, when i tested the regex, it is working fine. and it is here, but when i implement the same in my XSLT, it is throwing me an error.
The expected output is
<a href="245"/>
<a href="235"/>
<a href="345"/>,<a href="756"/>
please let me know how can i fix this.
Thanks

Related

idiomatic alternative to choose -> test -> value-of (XSLT 1.0)

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.

xsl:choose - Hardcoded 0's being added to end of XSLT string

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.

confusing error thown while trying to match templates

I've the below XML line of code.
<entry colname="col3" align="left" valign="top"><para>grandchild, cousin, <content-style font-style="italic">etc</content-style>., shall be described as “lawful” and “one of the next-of-kin” or “only next-of-kin”.</para></entry>
and below XSL
<xsl:template match="entry" name="entry">
<xsl:choose>
<xsl:when test="./#namest">
<xsl:variable name="namest" select="#namest" />
<xsl:variable name="nameend" select="#nameend" />
<xsl:variable name="namestPos" select="count(ancestor::tgroup/colspec[#colname=$namest]/preceding-sibling::colspec)" />
<xsl:variable name="nameendPos" select="count(ancestor::tgroup/colspec[#colname=$nameend]/preceding-sibling::colspec)" />
<td colspan="{$nameendPos - $namestPos + 1}" align="{#align}">
<xsl:apply-templates select="child::node()[not(self::page)]" />
</td>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:if test="./#morerows">
<xsl:attribute name="rowspan">
<xsl:value-of select="number(./#morerows)+1" />
</xsl:attribute>
</xsl:if>
<xsl:if test="./#align">
<xsl:attribute name="align">
<xsl:value-of select="#align" />
</xsl:attribute>
</xsl:if>
<xsl:if test="./#valign">
<xsl:attribute name="valign">
<xsl:value-of select="#valign" />
</xsl:attribute>
</xsl:if>
<xsl:for-each select="para">
<div class="para">
<xsl:choose>
<xsl:when test="../#colname='col3' and contains(./text(),'.')">
<xsl:variable name="strl">
<xsl:value-of select="fn:string-length(fn:substring-before(.,'.'))" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$strl < '6'">
<a href="{concat('er:#SCP_ORD_',//chapter/#num,'/','P',translate(./text(),'.','-'))}">
<xsl:value-of select="./text()" />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:for-each>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
and when i run this on my XML in the above mentioned line(the XML given in the sample), it is throwing me an error. and the error is as below.
Wrong occurrence to match required sequence type - Details: - XPTY0004: The supplied sequence ('2' item(s)) has the wrong occurrence to match the sequence type xs:string ('zero or one')
here what i actually was trying to achieve is, i have another table(which is basically a TOC), where in there is some linking needed, and below is such sample entries.
<entry colname="col3" align="right" valign="top"><para>A.1</para></entry>
<entry colname="col3" align="right" valign="top"><para>A.2</para></entry>
here i'm searching if the colname is col3 and if this has a . in it, and the above two cases mentioned are passing and are getting linked successfully, where in the case mentioned in the top is throwing the error, can anyone please suggest some better method to differentiate these two cases, and i use XSLT 2.0.
Thanks
The problem is
contains(./text(),'.')
./text() is not "the text of the current element" but rather the sequence of all text node children of the current element. In the case of
<para>grandchild, cousin, <content-style font-style="italic">etc</content-style>., shall be described as “lawful” and “one of the next-of-kin” or “only next-of-kin”.</para>
there are two such nodes, one containing everything between the <para> and <content-style> tags ("grandchild, cousin, " including the trailing space) and the other containing everything between the </content-style> and the </para>. But the contains function expects its first argument to be a single string, not a sequence of two nodes.
Instead of testing ./text() you probably just need to test .:
contains(., '.')
which is interpreted as the string value of the whole para element, i.e. a single string consisting of the concatenation of all the descendant text nodes (so the whole text "grandchild, cousin, etc., shall be described...").

anchor tag is not getting created correctly

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>

Using Multiple Nested Attributes in XSLT

What is the best way to construct nested attributes in XSL?
My issue is that onmouseover is an attribute and the src of img is an attribute. The current error given by the builder is:
An item of type 'Element' cannot be constructed within a node of type 'Attribute'.
I used to have an issue of multiple attributes which would have been my preferred route but throws an error:
Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added.
I have since attempted the following as a workaround but with no luck
<xsl:template name="Item3">
<xsl:param name="ItemID" />
<xsl:variable name="IMGSRC">
<xsl:choose>
<xsl:when test="$ItemID = 'ST-18/NM/NM/36'">
<xsl:value-of select="concat('imagesCategories/','ST-18-NM-NM-36','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/NM/NM/48'">
<xsl:value-of select="concat('imagesCategories/','ST-18-NM-NM-48','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/NM/NM/72'">
<xsl:value-of select="concat('imagesCategories/','ST-18-NM-NM-72','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/12'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-12','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/24'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-24','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/36'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-36','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/48'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-48','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/60'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-60','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/72'">
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('imagesCategories/',$ItemID,'.jpg')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="Items/Item[#ItemID=$ItemID]">
<xsl:attribute name="onmouseover">
<xsl:text>ddrivetip('</xsl:text>
<img src="{$IMGSRC}"/>
<br />
<b>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#ItemID" />
</b>
<br />
<b>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#ItemDescription" />
</b>
<br />
<br />
<xsl:text>Price (01-09): </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#PriceLevel1" />
<br/>
<xsl:text>Price (10-24): </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#PriceLevel2" />
<br/>
<xsl:text>Price (25-49): </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#PriceLevel3" />
<br/>
<xsl:text>Qty In Stock: </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#QtyOnHand" />
<br />
<br />
<xsl:text>Click </xsl:text>
<b>
<xsl:text>"BUY!"</xsl:text>
</b>
<xsl:text> to add this item to your shopping cart</xsl:text>
<xsl:text>', '', '300')</xsl:text>
</xsl:attribute>
There is some additional code and then the proper closing tags.
Thanks everyone!
It looks like you are trying to pass html as a string to your ddrivetip function. However, you are adding these as nodes instead of text, and nodes cannot be added added to attributes, so one solution is to make the nodes text (You'll have to escape the brackets and double quotes too).
However, you are putting a lot of information into the onmouseover event, which is not recommended. Instead of what you are currently doing, I would make a hidden element with an id that incorporates your itemId with the contents of your html and then show that as needed in your onmouseover event.
Use CDATA sections so that the XSLT Processor interpret your img tags as a part of text nodes and not as an attempt to insert element nodes into an attribute (it is forbidden by the XML specification)
<xsl:attribute name="onmouseover">
<xsl:text><![CDATA[ddrivetip('<img src="]]></xsl:text>
<xsl:value-of select="$IMGSRC" />
<xsl:text><![CDATA["/>
<br />
...