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>
Related
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
I have a parameterignoreAttributes which is a comma separated list of things to look for. I want to set a variable copyAttrib to be equal to whether any of them are exactly matched by name().
If xsl were a procedural language where variables could be reassigned, I'd use something like this:
<xsl:variable name="copyAttrib" select="true()">
<xsl:for-each select="tokenize($ignoreAttributes,',')">
<xsl:if test="compare(., name()) != 0">
<xsl:variable name="copyAttrib" select="false()"/>
</xsl:if>
</xsl:for-each>
Unfortunately, I can't do that, because xsl is functional (so says this other answer). So variables can only be assigned once.
I think the solution would look something like:
<vsl:variable name="copyAttrib">
<xsl:choose>
<xsl:when>
<xsl:for-each select="tokenize($ignoreAttributes, ',')">
<xsl:if test="compare(., name()) != 0"/>
</xsl:for-each>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Obviously not exactly that (otherwise I wouldn't be asking.)
I know that I could bypass the tokenize and for-each loop by just using replaces on ignoreAttributes and changing all the , to | and then using matches, but I'd like to avoid that if possible because then I need to deal with the possibility that ignoreAttributes (which the user provides) might contain some special characters that will change the regex pattern and escape them all.
I have a parameterignoreAttributes which is a comma separated list of things to look for. I want to set a variable copyAttrib to be equal to whether any of them are exactly matched by name().
That sounds to me like
<xsl:variable name="copyAttrib" as="xs:boolean"
select="tokenize($parameterignoreAttributes, ',') = name()"/>
You say:
Unfortunately, I can't do that, because xsl is functional
when what you mean is: "Fortunately, I don't need to do that, because XSLT is functional".
An XSLT-1.0 way of doing this is by using a recursive, named template:
<xsl:template name="copyAttrib">
<xsl:param name="attribs" />
<xsl:choose>
<xsl:when test="normalize-space(substring-before($attribs,',')) = normalize-space(name(.))">
<xsl:value-of select="'true'" />
</xsl:when>
<xsl:when test="normalize-space($attribs) = ''">
<xsl:value-of select="'false'" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="copyAttrib">
<xsl:with-param name="attribs" select="substring-after($attribs,',')" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Apply this template onto the current, the selected, node and wrap it in a <xsl:variable>:
<xsl:variable name="copyAttribResult">
<xsl:call-template name="copyAttrib">
<xsl:with-param name="attribs" select="'a,b,c,...commaSeparatedValues...'" />
</xsl:call-template>
</xsl:variable>
to get either true or false as a result.
I know the following xslt will work:
<xsl:attribute name="test">
<xsl:value-of select="substring(title, 1, 4000)"/>
</xsl:attribute>
But not sure what to do if there is something like the following and you want the substring over the whole attribute value not just the title or the substitle.
<xsl:attribute name="test">
<xsl:value-of select="title"/>
<xsl:if test="../../sub_title != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="../sub_title"/>
</xsl:if>
</xsl:attribute>
Is it even possible to apply a substring function over multiple lines that define an attribute?
I think what you are saying is that you want to build up a long string, consisting of the values of a number of other elements, and then truncate the result.
What you could do, is use the concat function to build the attribute value, and then do a substring on that.
<xsl:attribute name="test">
<xsl:value-of select="substring(concat(title, ' ', ../sub_title), 1, 4000)" />
</xsl:attribute>
In this case, if sub_title was empty, you would end up with a space at the end of the test attribute, so you might want to add a normalize-space to this expression
<xsl:value-of select="normalize-space(substring(concat(title, ' ', ../sub_title), 1, 4000))" />
An alternate approach, if you did want to use a more complicated expression, is to do the string calculation in a variable first
<xsl:variable name="test">
<xsl:value-of select="title"/>
<xsl:if test="../../sub_title != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="../sub_title"/>
</xsl:if>
</xsl:variable>
<xsl:attribute name="test">
<xsl:value-of select="substring($test, 1, 4000)" />
</xsl:attribute>
As an aside, you can simplify your code by using "Attribute Value Templates" here, instead of using the more verbose xsl:attribute command. Simply do this..
<myElement test="{substring($test, 1, 4000)}">
Here, the curly braces indicate an expression to be evaluated, rather than output literally.
I'm trying to create a variable that stores the value of an input string (TypeInput) in init cap form. This new variable will be used in different places in my stylesheet. I created a template that I call to convert the input string to init cap form. However, when I run the stylesheet, the resulting variable TypeInputInitCap shows up as NodeSet(1) in the debugger and doesn't output text in my output. Any ideas why? See sample below.
<xsl:variable name="TypeInputInitCap">
<xsl:call-template name="ConvertToInitCapString">
<xsl:with-param name="str" select="$TypeInput"></xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:template name="ConvertToInitCapString">
<xsl:param name="str"></xsl:param>
<!-- Extract each component of the name delimited by . -->
<xsl:variable name="TokenNodeSet">
<xsl:for-each select="tokenize($str, '.')">
<!-- Init cap each component -->
<xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"></xsl:value-of>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$TokenNodeSet">
<xsl:value-of select="."></xsl:value-of>
<xsl:if test="not(last())">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
I think that the problem is that the $TokenNodeSet variable contains just a single string, and so the second for-each just loops once.
What about doing this instead:
<xsl:template name="ConvertToInitCapString">
<xsl:param name="str"></xsl:param>
<xsl:for-each select="tokenize($str, '\.')">
<xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"/>
<xsl:if test="not(last())">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:for-each>
EDIT
Fixed the tokenize() call above as suggested by LarsH in the comments
I would replace
<xsl:variable name="TokenNodeSet">
<xsl:for-each select="tokenize($str, '.')">
<!-- Init cap each component -->
<xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"></xsl:value-of>
</xsl:for-each>
</xsl:variable>
with
<xsl:variable name="TokenNodeSet" select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" />
or better yet with
<xsl:variable name="TokenNodeSet" as="xs:string*" select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" />
or finally as it is XSLT 2.0 where there are no nodesets I would rename the variable as e.g.
<xsl:variable name="TokenSequence" as="xs:string*" select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" />
Thanks all for your help. The second part in my template was not necessary, so I'm now using this version, which works. It re-adds a '.' character between the tokens. (I didn't use the short version suggested in this thread because I will end up with an extra dot at the end if concatenated.):
<xsl:template name="ConvertToInitCapString">
<xsl:param name="str" select="."></xsl:param>
<!-- Extract each component of the name delimited by . -->
<xsl:for-each select="tokenize($str, '\.')">
<!-- Init cap each component -->
<xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"></xsl:value-of>
<xsl:if test="position() != last()">
<xsl:value-of select="'.'"></xsl:value-of>
</xsl:if>
</xsl:for-each>
</xsl:template>
I an trying to transform some data. The data contained in my xml field "Source/End/Edgecode" looks like -000016
I want to remove the leading dash. I thought I could use abs or right, but I don't know the correct syntax.
Thanks for any help...
* FROM CLIP NAME:
<xsl:template name="frame_to_tc">
<xsl:param name="frame"/>
<xsl:variable name="hh" select="($frame div $fph)"/>
<xsl:variable name="rh" select="($frame mod $fph)"/>
<xsl:variable name="mm" select="($rh div $fpm)"/>
<xsl:variable name="rm" select="($rh mod $fpm)"/>
<xsl:variable name="ss" select="($rm div $fps)"/>
<xsl:variable name="rs" select="($rm mod $fps)"/>
<xsl:variable name="ff" select="($rs mod $fps)"/>
<xsl:value-of select="format-number(floor($hh),'00')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="format-number(floor($mm),'00')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="format-number(floor($ss),'00')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="format-number(floor($ff),'00')"/>
<xsl:template name="abs">
<xsl:param name="input" select="0">
<xsl:variable name="num" select="number(input)" />
<xsl:choose>
<xsl:when test="$num >= 0">
<xsl:value-of select="$num" />
</xsl:when>
<xsl:when test="$num < 0">
<xsl:value-of select="-1 * $num" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$input" />
</xsl:otherwise>
</xsl:choose>
<xsl:template>
callable like this:
<xsl:variable name="absnum">
<xsl:call-template name="abs">
<xsl:with-param name="input" select="some/number" />
</xsl:call-template>
</xsl:variable>
You can use a simple substring.
<xsl:value-of select="substring($yourVariable, 2)"/>
<!-- In XSLT, the index starts from 1, so here you are taking a substring starting from the second character, in essence, leaving out the hyphen-->
Or even the node directly instead of the variable..
<xsl:value-of select="substring(/Your/Path/Here, 2)"/>
Since you are not specifying a specific number of characters to return, it will return the rest of the string completely. You can restrict the number of characters returned which is done by adding a comma and then specifying a second length upto which to cut) Ex: substring(/Your/Path/Here , 2, 5)
This will cut from the 2nd character upto five characters, If the string is "1234567" it will return "23456".
NOTE: This is assuming that you only want to remove the leading hyphen. If you want to remove the leading zeroes as well, a number() typecast should do the trick.
to remove the leading dash, try this XPath function:
<xsl:value-of select="substring-after($x,'-')"/>