how to check repeated elements in as string sequence/array? - xslt

I am using xslt 1.0 stylesheet to worlk on xml file data.
I have a variable in xslt which conatins many string separated by white space or new line charater.
i.e. the variable is "ServiceList", when I print it using follwong,
<xsl:value-of select="$ServiceList"/>
It prints following out put
hgd.sdf.gsdf sdf.sdh.duyg dsf.sdf.suos
jhs.sdu.sdfi
hdf.sdi.seij dsf.dsf.diuh
edr.sdi.sdhg dfh.dfg.dfg.fdg.idjf kjs.dfh.dfgj djg.dfs.dgji
I used follwing code to get each string separately.
<xsl:variable name="tokenizedSample" select="str:tokenize($ServiceList,'
')"/>
<xsl:for-each select="$tokenizedSample">
<xsl:variable name="serviceProvide" select="."/>
<xsl:variable name="tokenized1" select="str:tokenize($serviceProvide,' ')"/>
<xsl:for-each select="$tokenized1">
<xsl:variable name="serviceP" select="."/>
<xsl:value-of select="$serviceP"/>
</xsl:for-each>
</xsl:for-each>
the above code give me each string as separate one.
I have to chek is there any repeating string in above sequence/array. If it repeates it should show me the string is repeating.

This would be so much easier in XSLT 2.0
<xsl:variable name="tokenizedSample" select="tokenize($ServiceList, '
')"/>
<xsl:if test="count($tokenizedSample) != count(distinct-values($tokenizedSample))">...

Related

XSLT 2.0 how to test for position() in tokenize() on output

In XSLT 2.0 I have a parameter than comes in as a delimited string of document names like:
ms609_0080.xml~ms609_0176.xml~ms609_0210.xml~ms609_0418.xml
I tokenize() this string and cycle through it with xsl:for-each to pass each document to a key. The results from the key I then assemble into a comma-delimited string to output to screen.
<xsl:variable name="list_of_corresp_events">
<xsl:variable name ="tokenparam" select="tokenize($paramCorrespdocs,'~')"/>
<xsl:for-each select="$tokenparam">
<xsl:choose>
<xsl:when test=".[position() != last()]">
<xsl:value-of select="document(concat($paramSaxondatapath, .))/(key('correspkey',$correspid))/#xml:id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(document(concat($paramSaxondatapath, .))/(key('correspkey',$correspid))/#xml:id, ', ')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
Everything works fine except that when I output the variable $list_of_corresp_events it looks like the following, with an unexpected trailing comma:
ms609-0080-2, ms609-0176-1, ms609-0210-1, ms609-0418-1,
Ordinarily the last comma should not appear based on test=".[position() != last()]" ? Possibly positions don't work for tokenized data? I didn't see a way to apply string-join() to this.
Many thanks.
Improving on the solution from #zx485, try
<xsl:for-each select="$tokenparam">
<xsl:if test="position()!=1">, </xsl:if>
<xsl:value-of select="document(concat($paramSaxondatapath, .))/(key('correspkey',$correspid))/#xml:id"/>
</xsl:for-each>
Two things here:
(a) you don't need to repeat the same code in both conditional branches
(b) it's more efficient to output the comma separator before every item except the first, rather than after every item except the last. That's because evaluating last() involves an expensive look-ahead.
Change
<xsl:when test=".[position() != last()]">
to
<xsl:when test="position() != last()">
Then it should all work as desired.
It seems you can simplify this to
<xsl:variable name="list_of_corresp_events">
<xsl:value-of select="for $t in tokenize($paramCorrespdocs,'~') document(concat($paramSaxondatapath, $))/(key('correspkey',$correspid))/#xml:id" separator=", "/>
</xsl:variable>
or with string-join
<xsl:variable name="list_of_corresp_events" select="string-join(for $t in tokenize($paramCorrespdocs,'~') document(concat($paramSaxondatapath, $))/(key('correspkey',$correspid))/#xml:id, ', ')"/>

XSLT 2.0 tokenising delimiters within delimiters

In XSLT 2.0 I have long string (parameter) with a delimiter (;) inside a delimiter (~), more specifically a triplet inside a delimiter.
Data is organized like so:
<parameter>qrsbfs;qsvsv;tfgz~dknk;fvtea;gtvath~pksdi;ytbdi;oiunhu</parameter>
The first tokenize($mystring,'~') in a for-each produces :
qrsbfs;qsvsv;tfgz
dknk;fvtea;gtvath
pksdi;ytbdi;oiunhu
Within that tokenization, I need to treat it by looping again:
qrsbfs
qsvsv
tfgz
dknk
fvtea
gtvath
pksdi
ytbdi
oiunhu
I can do intensive string manipulation to get there using concat, string-length, and substring-before/substring-after, but I wondered if there wasn't a more elegant solution that my neophyte mind wasn't overlooking?
EDIT, adding nested tokenize that returned incorrect results:
<xsl:for-each select="tokenize($myparameter,'~')">
<xsl:for-each select="tokenize(.,';')">
<xsl:if test="position()=1">
<xsl:value-of select="."/>
</xsl:if>
<xsl:if test="position()=2">
<xsl:value-of select="."/>
</xsl:if>
<xsl:if test="position()=3">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
If you wanted a one line solution, you could do something like this, using nested for-in-return statements:
<xsl:sequence select="for $n in tokenize(.,'~') return concat(string-join(tokenize($n,';'),'
'),'
')"/>
If you don't need to tokenize them separately, you could replace the ~ with ; and tokenize all 9 elements at the same time:
tokenize(replace(parameter,'~',';'),';')
For what it's worth, the code in https://xsltfiddle.liberty-development.net/pPqsHUe uses
<xsl:template match="parameter">
<xsl:for-each select="tokenize(., '~')">
<xsl:value-of select="tokenize(., ';')" separator="
"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
and with output method text produces
qrsbfs
qsvsv
tfgz
dknk
fvtea
gtvath
pksdi
ytbdi
oiunhu

XSLT to convert elements in a concatenated line

I have this situation:
Two variables:
<xsl:variable name="varDep" select="DepAir"/>
<xsl:variable name="varArr" select="ArrAir"/>
Value of variables:
<testa>
<DepAir>SDU</DepAir>
<DepAir>CGH</DepAir>
</testa>
<testb>
<ArrAir>CGH</ArrAir>
<ArrAir>SDU</ArrAir>
</testb>
And I need transform in a concatenated line, like this:
<db:P_IAT>SDU;CGH;CGH;SDU;</db:P_IAT>
How can I do that?
Kind dynamic way will be to put loop for DepAir and ArrAir inside above variables as below:
<xsl:variable name="varDep">
<xsl:for-each select="//DepAir">
<xsl:value-of select="concat(., ';')"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="varArr">
<xsl:for-each select="//ArrAir">
<xsl:value-of select="concat(., ';')"/>
</xsl:for-each>
</xsl:variable>
And then use concat inside required node:
<required_node><xsl:value-of select="concat($varDep, $varArr)"/></required_node>
Note! In next questions please put your XSL (what you have tried) to avoid "Down Votes". Hope it will help in your case.

Split and concatenate a string in XSLT

Hi I have the below line in by XML and also I need a hyperlink for the number. I want this output to be shown in HTML format.
<main>
<alph>a b 2,3</alph>
</main>
I want an XSLT that gives output as:
a b 2, a b 3
I have tried the below XSLT:
<xsl:template match="alph">
<xsl:variable name="link" select="normalize-space(translate(
normalize-space(current()),abcdefghijklmnopqrstuvwxyz,''))"/>
<xsl:value-of select="substring-before(normalize-space(.),$link)"/>
<xsl:variable name="tex">
<xsl:value-of select="text()"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($link,',')">
<xsl:variable name="new">
<xsl:value-of select="tokenize($link,',')"/>
</xsl:variable>
<xsl:value-of select="concat($new,$tex)"/>
</xsl:when>
<xsl:when test="contains($link,'-')">
<xsl:value-of select="tokenize($link,'-')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$link"/>
</xsl:otherwise>
</xsl:choose>
But it is giving me output as:
a b 2 3a b 2,3
Thanks
One problem you have is with the variable link
<xsl:variable name="link" select="normalize-space(translate(
normalize-space(current()),abcdefghijklmnopqrstuvwxyz,''))"/>
It looks like you are trying removing all alphabetic characters from the string, so that you are left with just 2,3. However, for this to work the abc...xyz needs to be enclosed in apostrophes, otherwise it will be looking for an element named abc...xyz. Having said that, you say you are using XSLT2.0, so you can make use of the replace function here, which takes a regular expression as a parameter
<xsl:variable name="link" select="normalize-space(replace(current(),'[a-z]',''))"/>
Next, you can get the text before this link, like so
<xsl:variable name="text" select="normalize-space(substring-before(current(), $link))"/>
This will give you your a b
Finally, you can use the tokenize function to split up the 2,3. In your XSLT you seem to be looking for hyphens too, but the tokenize function also uses regular expressions, so this is not a problem. What you can do is just tokenize the string, and re-join it using the text variable as a separator
<xsl:value-of select="concat($text, ' ')"/>
<xsl:value-of select="tokenize($link,',|-')" separator="{concat(', ', $text, ' ')}"/>
Here is the full XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="alph">
<xsl:variable name="link" select="normalize-space(replace(current(),'[a-z]',''))"/>
<xsl:variable name="text" select="normalize-space(substring-before(current(), $link))"/>
<xsl:value-of select="concat($text, ' ')"/>
<xsl:value-of select="tokenize($link,',|-')" separator="{concat(', ', $text, ' ')}"/>
</xsl:template>
</xsl:stylesheet>
When applied on your XML, the following is output
a b 2, a b 3

XSL for each loop selecting position of first occurrence

In the following code snippet, I'm trying to get position of EMP_ID field from the available fields. This works fine if there's just one occurrence of EMP_ID.
But if there are more than one occurences then variable 'empid_field' will have positions of all the occurrences appended one after the other. i.e if EMP_ID is at postions 1, 8, and 11, then 'empid_field' would be '1811'.
Is there any way I get position of first occurrence only? Or Can I get comma separated positions atleast? (Code sample would be highly appreciated as I'm new to XSL programming)
<xsl:variable name="empid_field">
<xsl:for-each select="$fields">
<xsl:if test="internalName='EMP_ID'">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
The easiest solution which is in my mind is to extend this. But I think there are also solutions which look more pretty.
<xsl:variable name="empid_field">
<xsl:for-each select="$fields">
<xsl:if test="internalName='EMP_ID'">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="first_empid_field">
<xsl:value-of select="$empid_field[1]"/>
</xsl:variable>
The variable $first_empid_field will only have the first position value.
Ok got something ...
Created a comma separated string and picked the part before the delimiter.
<xsl:variable name="empid_fields" >
<xsl:for-each select="$fields">
<xsl:if test="internalName='EMP_ID'">
<xsl:value-of select="position()" />
<xsl:text >, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="empid_field" >
<xsl:value-of select="substring-before($empid_fields, ', ')" />
</xsl:variable>