Convert "15 Dec 2022" to 2022-12-15" using XSLT - xslt

I am trying to transform the following XML using XSLT
<?xml version="1.0" encoding="UTF-8"?>
<root>
<CandidateId>863</CandidateId>
<CaseId>2762833</CaseId>
<Status>Completed</Status>
<InitiatedDate>01 Dec 2022</InitiatedDate>
<CompletionDate>15 Jan 2022</CompletionDate>
<Comments />
</root>
My XSLT code is as follows:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node()">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="/">
<JobApplication>
<xsl:for-each select="root">
<JobApplication>
<xsl:variable name="month" select="substring(InitiatedDate,4,3)" />
<xsl:if test="$month = 'Dec'">
<xsl:variable name="m">12</xsl:variable>
</xsl:if>
<xsl:if test="$month = 'Nov'">
<xsl:variable name="m">11</xsl:variable>
</xsl:if>
<xsl:if test="$month = 'Oct'">
<xsl:variable name="m">10</xsl:variable>
</xsl:if>
<Bbgvdate><xsl:value-of select="concat(substring(InitiatedDate,8,4),'-',$m,'-',substring(InitiatedDate,1,2),'T00:00:00')"/></Bbgvdate>
<applicationId><xsl:value-of select="CandidateID"/></applicationId>
</JobApplication>
</xsl:for-each>
</JobApplication>
</xsl:template>
</xsl:stylesheet>
Now, I am unable to call the variable "m" for some reason in "Bbgvdate"
I am looking for a converted date that looks like "2022-12-15" (in yyyy-mm-dd format)
I get the error:
Unable to generate the XML document using the provided XML/XSL input. Errors were reported during stylesheet compilation
Need help with this.
Thanks

Related

XSL attirbutes with the same name

I am writing an xslt transformation for below XLS code:
<?xml version="1.0"?>
<OTA_HotelPmsInfoNotif xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" EchoToken="PMS" Version="0.101" PrimaryLangID="en" ClosureDate="2020-10-29" RetransmissionIndicator="true" SequenceNmbr="2" TimeStamp="2020-10-29T23:51:00Z">
<POS>
<Source>
<RequestorID Type="81" ID="POF" ID_Context="parol"/>
</Source>
<Source ISOCountry="CZ" ISOCurrency="CZE">
<RequestorID Type="10" ID="H1111" ID_Context="star">
<CompanyName>Pharmacy Prague</CompanyName>
</RequestorID>
</Source>
</POS>
</OTA_HotelPmsInfoNotif>
I would like to pull out of this XML attribute named ID but as you can see there are two ID attributes, ID="POF" and ID = "H1111". As for now i have what follows:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>RID</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="OTA_HotelPmsInfoNotif/POS/Source/RequestorID">
<xsl:text>"</xsl:text>
<xsl:if test ="#ID='H1111'">
<xsl:value-of select="#ID"/>
</xsl:if>
<xsl:text>";"</xsl:text>
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
But this code enters one empty line for ID="POF". How to extract only one atrribute ID="H1111"?
If you know the value of the Type attribute, you can do simply:
<xsl:value-of select="OTA_HotelPmsInfoNotif/POS/Source/RequestorID[#Type='10']/#ID"/>
Demo: https://xsltfiddle.liberty-development.net/aiyndY

XSL 1.0 If not like

I'm using XSL to create a PDF document template, and don't want certain fields to display if the line value is zero.
I have tried
<xsl:if test="line_value != 0">
<xsl:with-param name="value" select="unit_quantity"/>
</xsl:if>
But this doesn't work. I think because line_value is of the format £0.00.
I'm trying to get it to do line_value NOT LIKE '£0.00', but I don't think that's the correct syntax for XSL.
I am assuming that below is your XML:
INPUT:
<?xml version="1.0" encoding="utf-8" ?>
<body>
<line_value>£0.00</line_value>
<line_value>£1.00</line_value>
<line_value>£0.00</line_value>
<line_value>£2.00</line_value>
<line_value>£0.00</line_value>
<line_value>£5.00</line_value>
<line_value>£0.00</line_value>
<line_value>£1.00</line_value>
</body>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:for-each select="body/line_value">
<xsl:if test="number(translate(., '£', '')) != 0">
<num>
<xsl:value-of select="."/>
</num>
</xsl:if>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>

Very new to XSLT! Having difficulty adding new line between records

I have a soap xml output and need to convert it to plain text file. I am trying to use xsltproc. Got the following xsl tempalate online
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv">
<xsl:output method="text" encoding="utf-8" />
<xsl:strip-space elements="*" />
<xsl:variable name="delimiter" select="'|'" />
<csv:columns><column>Numbers</column></csv:columns>
<xsl:template match="getNumbersResponse">
<xsl:variable name="property" select="." />
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
My soap xml output is as follows
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body>
<ns4:getNumbersResponse xmlns:ns4="http://service.engine.com"><ns4:Numbers>100</ns4:Numbers>
<ns4:Numbers>200</ns4:Numbers>
</ns4:getNumbersResponse>
</soapenv:Body>
</soapenv:Envelope>
When I try xsltproc using the above xsl tempalate to transform this xml output, I get records in following format
100200
I want to add a new line between each record. Found online that adding following line should do it but I do not see any changes in the output with or without this line in xsl template.
<xsl:text>
</xsl:text>
I would want my output to be like this
Numbers|
100|
200|
Your stylesheet doesn't actually do anything, because your only template does not match anything in the source XML. The output you see is purely the result of the built-in template rules.
If you want to get a return-separated list of the ns4:Numbers values, you should do:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns4="http://service.engine.com">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/soapenv:Envelope">
<xsl:for-each select="soapenv:Body/ns4:getNumbersResponse/ns4:Numbers">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note the use of declared prefixes to address the nodes in your XML.
To get the result in your edited question, do:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns4="http://service.engine.com">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/soapenv:Envelope">
<xsl:text>Numbers|
</xsl:text>
<xsl:for-each select="soapenv:Body/ns4:getNumbersResponse/ns4:Numbers">
<xsl:value-of select="."/>
<xsl:text>|
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

XSLT manipulate text scattered across various nodes

Input file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!-- lower UPPER case -->
<document>
<rubbish> rubbish </rubbish>
<span class='lower'>
lower
<span class='upper'> upper </span>
case
</span>
</document>
Wanted output:
lower UPPER case
I know how to get the text included in the outer span with value-of, but this also
includes the string "upper" unchanged which is not what I want. I do not know how
to manipulate the text in the inner span and insert it in the middle of
the other text.
Failed attempt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="//span[#class = 'lower']">
<xsl:if test="span/#class = 'upper'">
<xsl:text>do something</xsl:text> <!--TO DO -->
</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You need to take a recursive approach here, for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="text()[parent::span]">
<xsl:choose>
<xsl:when test="../#class='upper'">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
To understand how this works, read up on built-in template rules: http://www.w3.org/TR/xslt/#built-in-rule
The following approach does away with the <choose> and completely pushes the problem down to the match expression:
<?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="text()"/>
<xsl:template match="text()[parent::span[#class = 'upper']]">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:template>
<xsl:template match="text()[parent::span[#class = 'lower']]">
<xsl:value-of select="translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
</xsl:template>
</xsl:stylesheet>

XSLT: How to reverse output without sorting by content

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