I need to be able to find the last occurrance of a number within an element.
For example,
<DATA1>States45bx3.33</DATA1>
<DATA2>States45bx33</DATA2>
From this string, I want to get the last number value in XSLT i.e 3.33 in 1st case and 33 in 2nd case.
Any help will be appreciated.
OK here's a solution in XSLT 1.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<xsl:for-each select="root/*">
<xsl:element name="{name()}">
<xsl:call-template name="getEndNumber">
<xsl:with-param name="input" select="normalize-space(.)"/>
<xsl:with-param name="dotIncluded" select="true()"/>
</xsl:call-template>
</xsl:element>
</xsl:for-each>
</output>
</xsl:template>
<xsl:template name="getEndNumber">
<xsl:param name="input"/>
<xsl:param name="dotIncluded"/>
<xsl:variable name="len" select="string-length($input)"/>
<xsl:choose>
<xsl:when test="substring($input, $len, 1) >= '0' and substring($input, $len, 1) <= '9'">
<xsl:call-template name="getEndNumber">
<xsl:with-param name="input" select="substring($input, 1, $len - 1)"/>
<xsl:with-param name="dotIncluded" select="$dotIncluded"/>
</xsl:call-template>
<xsl:value-of select="substring($input, $len, 1)"/>
</xsl:when>
<xsl:when test="substring($input, $len, 1) = '.' and $dotIncluded">
<xsl:call-template name="getEndNumber">
<xsl:with-param name="input" select="substring($input, 1, $len - 1)"/>
<xsl:with-param name="dotIncluded" select="false()"/>
</xsl:call-template>
<xsl:value-of select="substring($input, $len, 1)"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Applied to
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>States45bx3.33</a>
<b>States45bx33</b>
<c>asdfs12310.13.0sads23.23</c>
<d>asdfs12310.13.0sads23.23z</d>
<e>asdfs12310.13.0sads23.23.34</e>
</root>
the result is
<?xml version="1.0" encoding="UTF-8"?>
<output>
<a>3.33</a>
<b>33</b>
<c>23.23</c>
<d/>
<e>23.34</e>
</output>
If you are able to apply XSLT 2.0, then make use the following:
<xsl:value-of select="replace(., '.*[^.\d](\d*\.?\d+)$', '$1')"/>
This will return a number at the end of the current node, either an integer or a floating point one.
Explanation of the regex used:
(1) .* = arbitrary number of characters at the start of string
(2) [^.\d] = one character which is not a dot and not a digit
(3) (...) grouping, allowing refer-back by $1
(4) \d*.?\d+ = optional digits + optional dot + digits
(5) $ = end of string
(6) $1 refer-back to group 1
Put into an xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<xsl:for-each select="root/*">
<num>
<xsl:value-of select="replace(., '.*[^.\d](\d*\.?\d+)$', '$1')"/>
</num>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Applied to input
<?xml version="1.0" encoding="UTF-8"?>
<root>
<number>States45bx3.33</number>
<number>States45bx33</number>
<number>asdfs12310.13.0sads23.23</number>
</root>
This leads to
<?xml version="1.0" encoding="UTF-8"?>
<output>
<num>3.33</num>
<num>33</num>
<num>23.23</num>
</output>
You could put this into an xsl:function (XSLT 2.0 required for this also), so that you can simply retrieve the number at the end by calling the function, and have elements surrounding the result done outside the function:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:custom="http://customFunctions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<xsl:for-each select="root/*">
<num>
<xsl:value-of select="custom:getNumberAtEnd(.)"/>
</num>
</xsl:for-each>
</output>
</xsl:template>
<xsl:function name="custom:getNumberAtEnd">
<xsl:param name="input"/>
<xsl:value-of select="replace($input, '.*[^.\d](\d*.?\d+)$', '$1')"/>
</xsl:function>
</xsl:stylesheet>
Remark: when there is no number at the end of the string, the result of the function call or the earlier xsl:value-of statement will be the entire input string. When you don't want this, you will have to test the string first, or when you are certain that the end of the string will always be a well-formed number, then you can replace the regex used by the one below, which simply returns anything at the end after the final non-dot, non-digit, which may be an empty string.
<xsl:value-of select="replace($input, '.*[^.\d](.*)$', '$1')"/>
Related
Hello I have the tag below with & separated and I need to get each separate field in different tags to write later in separate fields in database, anyone with any examples in xslt 1.0?
<StatusMsg>
transfer-encoding=chunked&Server=nginx%2F1.13.12&MeusPedidosURL=http%3A%2F%2Fintegracao.meuspedidos.com.br%2Fapi%2Fv1%2Fclientes4118343%2F&Connection=keep-alive&MEUSPEDIDOS_QTDE_TOTAL_REGISTROS=0&Vary=Authorization%2C+Cookie&MeusPedidosID=4118343&Date=Tue%2C+31+Dec+2019+22%3A16%3A46+GMT&Content-Type=text%2Fplain
</StatusMsg>
Example of what I need ...
<MeusPedidosID>4118343</MeusPedidosID>
<MeusPedidosURL>http%3A%2F%2Fintegracao.meuspedidos.com.br%2Fapi%2Fv1%2Fclientes4118343%2F</MeusPedidosURL>
With a well-formed input such as:
XML
<StatusMsg>transfer-encoding=chunked&Server=nginx%2F1.13.12&MeusPedidosURL=http%3A%2F%2Fintegracao.meuspedidos.com.br%2Fapi%2Fv1%2Fclientes4118343%2F&Connection=keep-alive&MEUSPEDIDOS_QTDE_TOTAL_REGISTROS=0&Vary=Authorization%2C+Cookie&MeusPedidosID=4118343&Date=Tue%2C+31+Dec+2019+22%3A16%3A46+GMT&Content-Type=text%2Fplain</StatusMsg>
you can use the substring-before() and substring-after() functions to extract the wanted values - for example, the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="statusMsg" select="concat(StatusMsg, '&')" />
<output>
<MeusPedidosID>
<xsl:value-of select="substring-before(substring-after($statusMsg, 'MeusPedidosID='), '&')"/>
</MeusPedidosID>
<MeusPedidosURL>
<xsl:value-of select="substring-before(substring-after($statusMsg, 'MeusPedidosURL='), '&')"/>
</MeusPedidosURL>
</output>
</xsl:template>
</xsl:stylesheet>
will produce:
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<MeusPedidosID>4118343</MeusPedidosID>
<MeusPedidosURL>http%3A%2F%2Fintegracao.meuspedidos.com.br%2Fapi%2Fv1%2Fclientes4118343%2F</MeusPedidosURL>
</output>
If you want to extract all the fields encoded in the given string, you can use:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<xsl:call-template name="split">
<xsl:with-param name="string" select="concat(StatusMsg, '&')"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="split">
<xsl:param name="string"/>
<xsl:if test="contains($string, '=')">
<xsl:element name="{substring-before($string, '=')}">
<xsl:value-of select="substring-before(substring-after($string, '='), '&')" />
</xsl:element>
<!-- recursive call -->
<xsl:call-template name="split">
<xsl:with-param name="string" select="substring-after($string, '&')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
to get:
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<transfer-encoding>chunked</transfer-encoding>
<Server>nginx%2F1.13.12</Server>
<MeusPedidosURL>http%3A%2F%2Fintegracao.meuspedidos.com.br%2Fapi%2Fv1%2Fclientes4118343%2F</MeusPedidosURL>
<Connection>keep-alive</Connection>
<MEUSPEDIDOS_QTDE_TOTAL_REGISTROS>0</MEUSPEDIDOS_QTDE_TOTAL_REGISTROS>
<Vary>Authorization%2C+Cookie</Vary>
<MeusPedidosID>4118343</MeusPedidosID>
<Date>Tue%2C+31+Dec+2019+22%3A16%3A46+GMT</Date>
<Content-Type>text%2Fplain</Content-Type>
</output>
The below for-each loop is only picking up first value for X.. and not actually finding maximum could you please let me know the issue here.
<xsl:for-each select=".//X">
<xsl:choose>
<xsl:when test="position() = 1">
<xsl:value-of select="abc:set_variable($_XDOCTX, 'v1t1', abc:normalize_string($_XDOCTX, (.//.)[1]))"/>
</xsl:when>
<xsl:when test="abc:nOne(abc:normalize_string($_XDOCTX, (.//.)[1])) > number(abc:get_variable($_XDOCTX, 'v1t1'))">
<xsl:value-of select="abc:set_variable($_XDOCTX, 'v1t1', abc:normalize_string($_XDOCTX, (.//.)[1]))"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
To sort such large sequences of digits numerically, you can split them into substrings and sort by each substring individually - for example:
XML
<input>
<item>123456789.0123456780</item>
<item>123456789.0123456781</item>
<item>123456789.0123456782</item>
<item>123456789.0123456783</item>
<item>123456789.0123456784</item>
<item>123456789.0123456785</item>
<item>123456789.0123456786</item>
<item>123456789.0123456787</item>
<item>123456789.0123456788</item>
<item>123456789.0123456789</item>
<item>123456789.012344</item>
<item>123456789.012346</item>
</input>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/input">
<output>
<xsl:for-each select="item">
<xsl:sort select="substring-before(., '.')" data-type="number" order="descending"/>
<xsl:sort select="concat('0.', substring-after(., '.'))" data-type="number" order="descending"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<item>123456789.012346</item>
<item>123456789.0123456789</item>
<item>123456789.0123456788</item>
<item>123456789.0123456787</item>
<item>123456789.0123456786</item>
<item>123456789.0123456785</item>
<item>123456789.0123456784</item>
<item>123456789.0123456783</item>
<item>123456789.0123456782</item>
<item>123456789.0123456781</item>
<item>123456789.0123456780</item>
<item>123456789.012344</item>
</output>
Note that the limitation is processor-dependent: I could not reproduce the problem using either Xalan or Saxon.
<xsl:value-of select="substring-before($temp1,';')" disable-output-escaping="yes"/>
where temp1="fassdf sdf; asdf &dfsdfsdf;fsdfsf;"
The above code I am using to split value using ";". The problem is temp1 having &, so it splits this value by the escaped sequence character ;. So i am getting wrong output. But if I use the disable-output-escaping="yes" then the "&" is converted to &.
How to get the formatted value from the string? So if i split the string i will not get any issue. Because I will get string with & instead of &
Lets assume a sample XML for your/our convenience ..
<?xml version="1.0" encoding="utf-8"?>
<root>
<child>sharepoint; R&D;Department;</child>
</root>
XSLT code to output the desired one:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="text" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="child">
<xsl:call-template name="SplitString">
<xsl:with-param name="StringVal" select="concat(.,';')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="SplitString">
<xsl:param name="StringVal"/>
<xsl:variable name="first" select="substring-before($StringVal, ';')" />
<xsl:variable name="remaining" select="substring-after($StringVal, ';')" />
<xsl:value-of select="normalize-space($first)" disable-output-escaping="yes" />
<xsl:if test="$remaining">
<xsl:value-of select="'
'"/>
<xsl:call-template name="SplitString">
<xsl:with-param name="StringVal" select="$remaining" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This is the Output you get:
sharepoint
R&D
Department
e.g i have following strings:
xoc.coe.hw.ZSBALAJI
hw.cor.exp.nt.ZSSHIVA
i have to get only last string (i.e. ZSBALAJI from first and ZSSHIVA from second). How can I do it in xslt.
Thanks in advance.
Here is an XSLT-1.0 solution to your problem:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="//string">
<xsl:call-template name="skipper">
<xsl:with-param name="source" select="."/>
<xsl:with-param name="delimiter" select="'.'"/>
</xsl:call-template>
</xsl:template>
<!-- returns the substring after the last delimiter -->
<xsl:template name="skipper">
<xsl:param name="source"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($source,$delimiter)">
<xsl:call-template name="skipper">
<xsl:with-param name="source" select="substring-after($source,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$source"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to this document:
<?xml version="1.0" encoding="UTF-8"?>
<strings>
<string>xoc.coe.hw.ZSBALAJI</string>
<string>hw.cor.exp.nt.ZSSHIVA</string>
</strings>
It produces the following result:
ZSBALAJI
ZSSHIVA
Let's assume that you have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>xoc.coe.hw.ZSBALAJI</a>
<a>hw.cor.exp.nt.ZSSHIVA</a>
</root>
Then the following XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"/>
<xsl:template match="//a">
<xsl:variable name="parts" select="tokenize(node(), '\.')"/>
<xsl:variable name="count" select="count($parts)"/>
<xsl:for-each select="$parts">
<xsl:if test="position() = $count">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
will ouput
ZSBALAJI
ZSSHIVA
Essentially, you can use XPath tokenize function and then take the last token.
You can try and use EXSLT tokenize(string, string?) function to split by '.' on the relevant node, see this for additional info.
I have a list of items:
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
and I want as output
z
c
x
a
I have no order information in the file and I just want to reverse the lines. The last line in the source file should be first line in the output. How can I solve this problem with XSLT without sorting by the content of the items, which would give the wrong result?
I will present two XSLT solutions:
I. XSLT 1.0 with recursion Note that this solution works for any node-set, not only in the case when the nodes are siblings:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:call-template name="reverse">
<xsl:with-param name="pList" select="*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="pList"/>
<xsl:if test="$pList">
<xsl:value-of
select="concat($pList[last()], '
')"/>
<xsl:call-template name="reverse">
<xsl:with-param name="pList"
select="$pList[not(position() = last())]"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</t>
produces the wanted result:
z
c
x
a
II. XSLT 2.0 solution :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:value-of select="reverse(*)/string(.)"
separator="
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document, the same correct result is produced.
XML CODE:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<device>
<element>a</element>
<element>x</element>
<element>c</element>
<element>z</element>
</device>
XSLT CODE:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//device">
<xsl:for-each select="element">
<xsl:sort select="position()" data-type="number" order="descending"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
note: if you're using data-type="number", and any of the values aren't numbers, those non-numeric values will sort before the numeric values. That means if you're using order="ascending", the non-numeric values appear first; if you use order="descending", the non-numeric values appear last.
Notice that the non-numeric values were not sorted; they simply appear in the output document in the order in which they were encountered.
also, you may find usefull to read this:
http://docstore.mik.ua/orelly/xml/xslt/ch06_01.htm
Not sure what the full XML looks like, so I wrapped in a <doc> element to make it well formed:
<doc>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</doc>
Running that example XML against this stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:call-template name="reverse">
<xsl:with-param name="item" select="doc/item[position()=last()]" />
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="item" />
<xsl:value-of select="$item" />
<!--Adds a line feed-->
<xsl:text>
</xsl:text>
<!--Move on to the next item, if we aren't at the first-->
<xsl:if test="$item/preceding-sibling::item">
<xsl:call-template name="reverse">
<xsl:with-param name="item" select="$item/preceding-sibling::item[1]" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Produces the requested output:
z
c
x
a
You may need to adjust the xpath to match your actual XML.
Consider this XML input:
<?xml version="1.0" encoding="utf-8" ?>
<items>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</items>
The XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/items[1]">
<xsl:variable name="items-list" select="." />
<xsl:variable name="items-count" select="count($items-list/*)" />
<xsl:for-each select="item">
<xsl:variable name="index" select="$items-count+1 - position()"/>
<xsl:value-of select="$items-list/item[$index]"/>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And the result:
z
c
x
a