Remove all single characters from string using xslt1.0 - xslt-1.0

<title>
<article_title>Land a b c d Band</article_title>
</title>
using the following function
replace(article_title, '(^[^ ]+)(.+\s+)([^ ]+)$', '$1 $3')
this string in is transformed to Land Band which is exactly what i want.
but the problem is i need this solution in xslt 1.0 since the java app that i am working with can only handle xslt 1.0 parsing.

This XSLT 1.0 transformation (there is a nasty SO bug and the code isn't indented -- I apologize for this visual mess...):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" name="removeSingles">
<xsl:param name="pText" select="."/>
<xsl:variable name="vText" select="normalize-space($pText)"/>
<xsl:if test="string-length($vText)">
<xsl:variable name="vLeftChars" select=
"substring-before(concat($vText, ' '), ' ')"/>
<xsl:if test="string-length($vLeftChars) >1">
<xsl:value-of select="$vLeftChars"/>
<xsl:if test=
"not(string-length($vLeftChars)
>=
string-length($vText)
)
">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:if>
<xsl:call-template name="removeSingles">
<xsl:with-param name="pText" select=
"substring-after($vText, ' ')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<title>
<article_title>Land a b c d Band</article_title>
</title>
produces the wanted, correct result:
<title>
<article_title>Land Band</article_title>
</title>

Related

find a right xslt for given data

data xml form and conver and filtering some information...
using axes and following sibling...
-----------------------------Source XML------------------------ <FY2019Temperature>
<January>
<High>-1</High>
<Low>-9</Low>
</January>
<February>
<High>-1</High>
<Low>-9</Low>
</February>
<March>
<High>21</High>
<Low>9</Low>
</March>
<April>
<High>21</High>
<Low>9</Low>
</April>
<May>
<High>21</High>
<Low>9</Low>
</May>
<June>
<High>21</High>
<Low>9</Low>
</June>
<July>
<High>29</High>
<Low>18</Low>
</July>
<August>
<High>29</High>
<Low>18</Low>
</August>
<September>
<High>24</High>
<Low>12</Low>
</September>
<October>
<High>24</High>
<Low>12</Low>
</October>
<November>
<High>12</High>
<Low>-1</Low>
</November>
<December>
<High>-1</High>
<Low>-9</Low>
</December>
-----------------Expected Result---------------------------
Months Range,Temperature Range
January to February,-1 to -9
March to June,21 to 9
July to August,29 to 18
September to October,24 to 12
November,12 to -1
December,-1 to -9
The XSLT 3 approach outlined in the comment would be
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:text>Months range, temperature range
</xsl:text>
<xsl:for-each-group select="*" composite="yes" group-adjacent="High, Low">{name()}{(' to ' || name(current-group()[last()]))[current-group()[2]]}, {High} to {Low}
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Solution-1
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output encoding="UTF-8" method="text" />
<xsl:variable name="Linefeed" select="'
'"/>
<xsl:template match="/">
<xsl:value-of select="string-join(('Months Range','Temperature Range'),
',')"/>
<xsl:value-of select="$Linefeed"/>
<xsl:for-each-group select="/FY2019Temperature/*" group-
adjacent="string-join((High,Low),'|')">
<xsl:for-each select="current-group()">
<xsl:choose>
<xsl:when test="count(current-group()) gt 1">
<xsl:if test="position()=1">
<xsl:value-of select="name()"/>
<xsl:text> to </xsl:text>
</xsl:if>
<xsl:if test="position()=last()">
<xsl:value-of select="name()"/>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()"/>
<xsl:text>, </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:value-of select="High"/>
<xsl:text> to </xsl:text>
<xsl:value-of select="Low"/>
<xsl:value-of select="$Linefeed"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 multiple call of a template

I need to replace strings in an XML file. I'm trying to use XSLT to do it. I want to use call-template for each string I need to replace.
When I make multiple call to the template, only the last call works fine.
The XML file I need to change : I want to replace the strings
‘
and
—
by spaces
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>start test_145 : ‘ and test_ 151 : — _end_test</reason>
</RAPPORT>
The template I use :
<xsl:template name="globalReplace">
<xsl:param name="outputString"/>
<xsl:param name="target"/>
<xsl:param name="replacement"/>
<xsl:choose>
<xsl:when test="contains($outputString,$target)">
<xsl:value-of select="concat(substring-before($outputString,$target),$replacement)"/>
<xsl:call-template name="globalReplace">
<xsl:with-param name="outputString" select="substring-after($outputString,$target)"/>
<xsl:with-param name="target" select="$target"/>
<xsl:with-param name="replacement" select="$replacement"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$outputString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The multiple calls :
<xsl:template match="text()">
<xsl:call-template name="globalReplace">
<xsl:with-param name="outputString" select="."/>
<xsl:with-param name="target" select="'‘'"/>
<xsl:with-param name="replacement" select="' '"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="globalReplace">
<xsl:with-param name="outputString" select="."/>
<xsl:with-param name="target" select="'—'"/>
<xsl:with-param name="replacement" select="' '"/>
</xsl:call-template>
</xsl:template>
Expected result :
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>debut test_test_145 et test 151 _fin test</reason>
</RAPPORT>
What I get in fact :
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>debut test_test_145**PUI** et test 151 _fin test</reason>
</RAPPORT>
PUI means an unexpected character instead of the needed space
You have two templates that match text(). Only the last of these is applied - see: https://www.w3.org/TR/1999/REC-xslt-19991116#conflict
In the given example, you could simply use the translate() function to do all the work at once, since the "strings" you want to replace are actually characters:
XML
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>start test_145 : ‘ and test_ 151 : — _end_test</reason>
</RAPPORT>
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="translate(., '‘—', ' ')"/>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>start test_145 : and test_ 151 : _end_test</reason>
</RAPPORT>
Many thanks !
It works fine this way !
I improved it inserting my result into a variable :
<xsl:template match="text()">
<xsl:variable name="premierePasse">
<xsl:value-of select="translate(., '‘—’',' ')"/>
</xsl:variable>
<xsl:value-of select="translate($premierePasse, 'œ', 'œ')"/>
</xsl:template>

Apply templates that matchs two conditions

I need to list all the INST names but only if the "onlyTesters" node don´t exists in the "inst/idef" part of XML body above.
I know thats strange but I can´t change the XML I receive.
XML:
<river>
<station num="699">
<inst name="FLU(m)" num="1">
<idef></idef>
</inst>
<inst name="Battery(V)" num="18">
<idef>
<onlyTesters/>
</idef>
</inst>
</station>
<INST name="PLU(mm)" num="0" hasData="1" virtual="0"/>
<INST name="FLU(m)" num="1" hasData="1" virtual="0"/>
<INST name="Q(m3/s)" num="3" hasData="1" virtual="1"/>
<INST name="Battery(V)" num="18" hasData="1" virtual="0"/>
</river>
XSL:
<xsl:template match="/">
<xsl:apply-templates select="//INST[#hasData = 1 and not(//inst[#num=(current()/#num)]/idef/onlyTesters)]/#name"/>
</xsl:template>
<xsl:template match="//INST[#hasData = 1 and not(//inst[#num=(current()/#num)]/idef/onlyTesters)]/#name">
<xsl:value-of select="#name"/>,
</xsl:template>
I´m having no match.
This is the result I expect:
PLU(mm),FLU(m),Q(m3/s)
You can achieve this with only one template:
<xsl:template match="/">
<xsl:for-each select="//INST[#hasData='1' and not(#name=//inst[idef/onlyTesters]/#name)]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
Output is:
PLU(mm), FLU(m), Q(m3/s)
Cross-references are best resolved using a key - for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:key name="inst" match="inst" use="#name" />
<xsl:template match="/river">
<xsl:for-each select="INST[#hasData = 1 and not(key('inst', #name)/idef/onlyTesters)]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Or even simpler:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:key name="exclude" match="onlyTesters" use="ancestor::inst/#name" />
<xsl:template match="/river">
<xsl:for-each select="INST[#hasData = 1 and not(key('exclude', #name))]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Modifying the XML element value upon condiion by XSLT

I have xml
<PhoneNumberDetails>
<PhoneNumber>
<Number>4162880001</Number>
<TimeStamp>2016-08-16T07:07:44-04:00</TimeStamp>
</PhoneNumber>
<PhoneNumber>
<Number>4162880002</Number>
<TimeStamp>2016-08-16T07:07:44-04:00</TimeStamp>
</PhoneNumber>
</PhoneNumberDetails>
This is input to a XSL. In this XSL i retrieve a phone number from context variable stored in one of the previous XSLT and if it matches to any phone number in above xml, I need to update the TimeStamp to new current Timestamp. I need help in this part.
The XSLT Code:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" xmlns:date="http://exslt.org/dates-and-times" extension-element-prefixes="dp date" exclude-result-prefixes="dp">
<xsl:variable name="currentTimeStamp" select="date:date-time()"/>
<xsl:output method="xml"/>
<xsl:template match="PhoneNumberDetails">
<xsl:variable name="savedPhoneNum" select="dp:variable('var://context/name/phonenum')"/>
<xsl:choose>
<xsl:when test="/*[local-name()='PhoneNumberDetails']/*[local-name()='PhoneNumber']/*[local-name()='Number']=$savedPhoneNum">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="phonenumber"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="phonenumber" match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PhoneNumberDetails">
<PhoneNumberDetails>
<xsl:apply-templates select="#* | *"/>
<PhoneNumber>
<Number>
<xsl:value-of select="dp:variable('var://context/name/phonenum')"/>
</Number>
<TimeStamp>
<xsl:value-of select="$currentTimeStamp"/>
</TimeStamp>
</PhoneNumber>
</PhoneNumberDetails>
</xsl:template>
</xsl:stylesheet>
If the phone number does not matches any number in XML i need to add a new element to the above xml and this part works. For e.g.
<PhoneNumber>
<Number>New number</Number>
<TimeStamp>Current TimeStamp</TimeStamp>
</PhoneNumber>
Why can't you do simply:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="dp date">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="savedPhoneNum" select="dp:variable('var://context/name/phonenum')"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TimeStamp[../Number=$savedPhoneNum]">
<xsl:copy>
<xsl:value-of select="date:date-time()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PhoneNumberDetails[not(PhoneNumber/Number=$savedPhoneNum)]">
<xsl:copy>
<xsl:apply-templates/>
<PhoneNumber>
<Number>New number</Number>
<TimeStamp>Current TimeStamp</TimeStamp>
</PhoneNumber>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I suggest you start with something like this. Don't use an absolute path to select the phone number, use it from that context node where the template matches.
<xsl:template match="PhoneNumber">
<xsl:copy>
<xsl:copy-of select="Number"/>
<xsl:variable name="savedPhoneNum" select="dp:variable('var://context/name/phonenum')"/>
<TimeStamp>
<xsl:choose>
<xsl:when test="Number=$savedPhoneNum">
<xsl:value-of select="$currentTimeStamp"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="TimeStamp"/>
</xsl:otherwise>
</xsl:choose>
</TimeStamp>
</xsl:copy>
</xsl:template>
Note that I assume that you don't have a (non-shown) namespace on the elements of your input xml. If yes, keep your *[local-name()=""] construct, but then the namespace would have to be added to the match="PhoneNumber" and to the TimeStamp output as well.

Export html to csv using xslt - Handle comma and special characters

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*" />
<xsl:variable name="delimiter" select="','" />
<xsl:template match="/"><xsl:apply-templates/></xsl:template>
<xsl:template match="TR"><xsl:apply-templates/><xsl:text>
</xsl:text> </xsl:template>
<xsl:template match="TD">
<xsl:for-each select="#*[not(name() = 'class' or name() = 'style')]">
<xsl:attribute name="{name()}"><xsl:value-of select="."/> </xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:template>
</xsl:stylesheet>
I am using above XSL to convert HTML to CSV but if TD element contains comma it is being treated as separate column,I need to check if TD element data contains comma(,) if yes include data in double quotes.How do I achieve this?