What's the options to show/output date in HTML in some sort of readable format. I mean if I simply show value of date_last_activity it looks wierd :S
So my question IS does XSLT offers any date format conversions at all?
XML:
<date_last_activity format="MMDDCCYY">04132012</date_last_activity>
XLS template:
<xsl:value-of select="date_last_activity"/>
HTML:
04132012
Would rather like it to look like 04/13/2012...
I readed a lot on XSLT one, I guess it's possible to make "template" => formatDate... but no idea where to start :S
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="date_last_activity">
<xsl:value-of select=
"concat(
substring(.,
string-length(substring-before(#format, 'MM'))+1,
2
),
'/',
substring(.,
string-length(substring-before(#format, 'DD'))+1,
2
),
'/',
substring(.,
string-length(substring-before(#format, 'CCYY'))+1,
4
)
)"/>
</xsl:template>
</xsl:stylesheet>
when applied to the provided XML document:
<date_last_activity format="MMDDCCYY">04132012</date_last_activity>
produces the wanted, correct result:
04/13/2012
When the same transformation is applied to the following XML document:
<date_last_activity format="CCYYDDMM">20121304</date_last_activity>
the same correct result is produced:
04/13/2012
XSLT 2.0 offers the function format-date(). However, before using it you will need to convert your date to ISO format, YYYY-MM-DD, which can be done using concat and substring.
If you're still stuck with XSLT 1.0, there's a library of date-handling functions and templates available at www.exslt.org.
If you need to do it n more than one place, create a named template somewhere in your xsl
EDIT: template didn't appear because of wrong formatting...
<xsl:template name="formats_date">
<xsl:param name="raw_date" />
<xsl:value-of select="concat( substring( $raw_date, 0, 2 ), '/', substring( $raw_date, 2, 2 ), '/', substring( $raw_date, 4 ) )" />
</xsl:template>
Then apply it as
<xsl:call-template name="formats_date">
<xsl:with-param name="raw_date" select="date_last_activity" />
</xsl:call-template/>
Otherwise you can just do the xsl:value-of concat... stuff directly where you need it
Related
I'm writing a PL/SQL tool for Oracle to get an XML Spreadsheet out from a cursor.
After a few days I've come to the conclusion that the simplest way to get it done is just relay on the XML features the Oracle DB offers.
So, my idea is just to format the query result in XML and then get an Excel Spreadsheet via XSLT.
So far, so good: I managed to get my spreadsheet from the query I wrote but I need to solve a little issue.
I want the ability to set the column headings in Excel just setting aliases on my columns, but when I use characters not compliant with the XML language I obviously get something different from what I mean.
select dbms_xmlgen.getxmltype(
'select first_name as "First na/Me"
from hr.employees
where department_id = 100;') from dual;
generates:
<ROWSET>
<ROW>
<First_x0020_na_x002F_Me>Nancy</First_x0020_na_x002F_Me>
</ROW>
<ROWSET>
Nothing prevents me from getting my Excel though, I then transform the XML document above via XSLT to an Excel 2007 compatible workbook containing this node
<Cell>
<Data ss:Type="String">First_x0020_na_x002F_Me</Data>
</Cell>
At this point I just need to instruct my XSLT ti search in the column headings for "x\uFFFF", extract the hexadecimal code and turn it into the corresponding character, but how can this be done?
As mentioned in the title, my XSLT is 1.0 compliant but I can upgrade to an upper version if necessary and I cannot upgrade due to unsupported native support for XSLT 2.0 in Oracle DB.
At this point I just need to instruct my XSLT to search in the column
headings for "x\uFFFF", extract the hexadecimal code and turn it into
the corresponding character, but how can this be done?
As mentioned in the title, my XSLT is 1.0 compliant but in can upgrade
to an upper version if necessary.
This XSLT 2.0 transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[matches(., '_x(\d|[A-Fa-f]){4}_')]">
<xsl:analyze-string select="." regex="_x((\d|[A-Fa-f]){{4}})_" >
<xsl:matching-substring>
<xsl:value-of select="codepoints-to-string(my:hex-to-int(regex-group(1), 0))"/>
</xsl:matching-substring>
<xsl:non-matching-substring><xsl:value-of select="."/></xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:function name="my:hex-to-int" as="xs:integer">
<xsl:param name="pHex" as="xs:string"/>
<xsl:param name="pResult" as="xs:integer"/>
<xsl:sequence select=
"if(not($pHex))
then $pResult
else
my:hex-to-int(substring($pHex, 2),
16*$pResult + my:hex-digit-to-int(substring($pHex, 1, 1)))
"/>
</xsl:function>
<xsl:function name="my:hex-digit-to-int" as="xs:integer">
<xsl:param name="pHexDigit" as="xs:string"/>
<xsl:sequence select=
"index-of(string-to-codepoints('0123456789abcdef'),
string-to-codepoints(lower-case($pHexDigit)))[1] -1"/>
</xsl:function>
</xsl:stylesheet>
When applied on the provided XML document:
<Cell xmlns:ss="some:ss">
<Data ss:Type="String">First_x0020_na_x002F_Me</Data>
</Cell>
produces the wanted, correct result:
<Cell xmlns:ss="some:ss">
<Data ss:Type="String">First na/Me</Data>
</Cell>
Note:
A similar solution in XSLT 1.0 would be possible, if we could use an extension function that takes an integer and returns the Unicode character that has as its code this integer -- exactly what the XPath 2.0 codepoints-to-string() function is doing.
As for escaping "unsafe HTML characters", just specifying:
<xsl:output method="html"/>
or
<xsl:output method="xhtml"/>
should ensure that the unsafe characters are represented properly.
I have a XSL file in which I am creating a field like this:
<ServiceText>
<xsl:value-of select="concat(Yrs,'-',Mos,'-',Days,'-',Hrs)" />
</ServiceText>
The values of 'Yrs,'-',Mos,'-',Days,'-',Hrs , I am receiving from a Web service response and assiging it to the XSL directly. I cannot do any modification to the data in code for these fields, because that is how the ocde is. All data manipulation is on the xslt.
I want to do a data filtering on xslt as follows:
if value of yrs =-1 then yrs=""
if value of mos =-1 then mos=""
if value of Days =-1 then Days=""
if value of Hrs =-1 then Hrs=""
How can I do it on the XSL file?
XSLT 2.0:
<xsl:template match="/root">
<ServiceText>
<xsl:value-of select="string-join((Yrs, Mos, Days, Hrs)[.!=-1],'-')" />
</ServiceText>
</xsl:template>
See link for a Working example
In XSLT 1.0 use something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*/*[not(.= -1)]">
<xsl:value-of select="concat(., substring('-', 2 - not(self::Hrs)))"/>
</xsl:template>
<xsl:template match="*/*[. = -1]"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>
<Yrs>-1</Yrs>
<Mos>7</Mos>
<Days>15</Days>
<Hrs>3</Hrs>
</t>
the wanted result is produced:
7-15-3
Do Note:
It seems that there is an assumption that the "-1" values form a contiguous group (right to left in the sequence Yrs, Mos,Days).
If this assumption is violated, it would be impossible to understand what is the missing part in 2013-10-8 -- is it the months or the days ?
I am using XSLT to create XML file. A Date-time has milliseconds. I need to have the output XML without milliseconds.
Format needs to be YYYY-MM-DDTHH:MM:SS
For example:
XML shows date as: 2012-12-341T09:26:53.132-0500
But this needs to be: 2012-12-341T09:26:53
If all of the values are dateTime and have a ., you could use substring-before():
substring-before('2012-12-341T09:26:53.132-0500', '.')
Of you could use substring() to select the first 20 characters:
substring('2012-12-341T09:26:53.132-0500', 0, 21)
If you are using XSLT2, see this function: http://www.w3.org/TR/xslt20/#function-format-dateTime. This picture string should give you what you want:
format-dateTime($dateTime,'[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]')
This XPath expression produces the wanted result regardless whether the string contains a dot or a hyphen or both dot and hyphen, or none, and doesn't rely on the number of digits used for year, month, day:
substring-before(concat(substring-before(concat(substring-after(.,'T'),
'.'),
'.'),
'-'),
'-')
Here is a simple XSLT transformation that uses this XPath expression:
<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="dt/text()">
<xsl:value-of select="substring-before(., 'T')"/>
<xsl:text>T</xsl:text>
<xsl:value-of select=
"substring-before(concat(substring-before(concat(substring-after(.,'T'),
'.'),
'.'),
'-'),
'-')
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this test XML document:
<t>
<dt>2012-12-341T09:26:53.132-0500</dt>
<dt>2012-12-355T09:34:56</dt>
<dt>2012-12-355T09:34:56-0500</dt>
<dt>2012-12-13T9:34:5-0500</dt>
<dt>2012-12-344T09:12:34.378-0500</dt>
</t>
the wanted, correct result is produced:
<t>
<dt>2012-12-341T09:26:53</dt>
<dt>2012-12-355T09:34:56</dt>
<dt>2012-12-355T09:34:56</dt>
<dt>2012-12-13T9:34:5</dt>
<dt>2012-12-344T09:12:34</dt>
</t>
Explanation:
Proper application of sentinels.
I am newbie to xslt. My requirement is to transform xml file into text file as per the business specifications. I am facing an issue with one of the string formatting issue. Please help me out if you have any idea.
Here is the part of input xml data:
"0001295"
Expected result to print into text file:
1295
My main issue is to remove leading Zeros. Please share if you have any logic/function.
Just use this simple expression:
number(.)
Here is a complete example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="t">
<xsl:value-of select="number(.)"/>
</xsl:template>
</xsl:stylesheet>
When applied on this XML document:
<t>0001295</t>
the wanted, correct result is produced:
1295
II. Use format-number()
format-number(., '#')
There are a couple of ways you can do this. If the value is entirely numeric (for example not a CSV line or part of a product code such as ASN0012345) you can convert from a string to a number and back to a string again :
string(number($value)).
Otherwise just replace the 0's at the start :
replace( $value, '^0*', '' )
The '^' is required (standard regexp syntax) or a value of 001201 will be replaced with 121 (all zero's removed).
Hope that helps.
Dave
Here is one way you could do it in XSLT 1.0.
First, find the first non-zero element, by removing all the zero elements currently in the value
<xsl:variable name="first" select="substring(translate(., '0', ''), 1, 1)" />
Then, you can find the substring-before this first character, and then use substring-after to get the non-zero part after this
<xsl:value-of select="substring-after(., substring-before(., $first))" />
Or, to combine the two statements into one
<xsl:value-of select="substring-after(., substring-before(., substring(translate(., '0', ''), 1, 1)))" />
So, given the following input
<a>00012095Kb</a>
Then using the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/a">
<xsl:value-of select="substring-after(., substring-before(., substring(translate(., '0', ''), 1, 1)))" />
</xsl:template>
</xsl:stylesheet>
The following will be output
12095Kb
As a simple alternative in XSLT 2.0 that can be used with numeric or alpha-numeric input, with or without leading zeros, you might try:
replace( $value, '^0*(..*)', '$1' )
This works because ^0* is greedy and (..*) captures the rest of the input after the last leading zero. $1 refers to the captured group.
Note that an input containing only zeros will output 0.
XSLT 2.0
Remove leading zeros from STRING
<xsl:value-of select="replace( $value, '^0+', '')"/>
You could use a recursive template that will remove the leading zeros:
<xsl:template name="remove-leading-zeros">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="starts-with($text,'0')">
<xsl:call-template name="remove-leading-zeros">
<xsl:with-param name="text"
select="substring-after($text,'0')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Invoke it like this:
<xsl:call-template name="remove-leading-zeros">
<xsl:with-param name="text" select="/path/to/node/with/leading/zeros"/>
</xsl:call-template>
</xsl:template>
<xsl:value-of select="number(.) * 1"/>
works for me
All XSLT1 parser, like the popular libXML2's module for XSLT, have the registered functions facility... So, we can suppose to use it. Suppose also that the language that call XSLT, is PHP: see this wikibook about registerPHPFunctions.
The build-in PHP function ltrim can be used in
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://php.net/xsl">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="test">
show <xsl:value-of select="fn:function('ltrim',string(.),'0')" />",
</xsl:template>
</xsl:stylesheet>
Now imagine a little bit more complex problem, to ltrim a string with more than 1 number, ex. hello 002 and 021, bye.
The solution is the same: use registerPHPFunctions, except to change the build-in function to a user defined one,
function ltrim0_Multi($s) {
return preg_replace('/(^0+|(?<= )0+)(?=[1-9])/','',$s);
}
converts the example into hello 2 and 21, bye.
I was wondering whether it's possible to sort some elements first and store them (already sorted) in a variable. I would need to refer to them thought XSLT that's why I'd like to store them in a variable.
I was trying to do the following, but it doesn't seem to work
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:variable name="deposits">
<xsl:for-each select="/BookingCostings/MultiDeposits">
<xsl:sort select="substring(#DepositDate, 1, 4)" />
<xsl:sort select="substring(#DepositDate, 6, 2)" />
<xsl:sort select="substring(#DepositDate, 9, 2)" />
</xsl:for-each>
</xsl:variable>
I was trying to sort the elements by #DepositDate in the format 'yyyy-mm-dd' and store them all in the $deposits variable. So that later, I could access them using $deposits[1].
I would appreciate any help and tips!
Thanks a lot!
Using XSLT version 2.0 you could use perform-sort and tell that your variable is of type of a sequence of MultiDeposits using the as keyword (as="element(MultiDeposits)+")
Since your data is already as yyyy-mm-dd you can avoid to use the subtring to get each part of the date and use the sort directly on the field
with this sample xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<BookingCostings>
<MultiDeposits depositDate="2001-10-09">1</MultiDeposits>
<MultiDeposits depositDate="1999-10-09">2</MultiDeposits>
<MultiDeposits depositDate="2010-08-09">3</MultiDeposits>
<MultiDeposits depositDate="2010-07-09">4</MultiDeposits>
<MultiDeposits depositDate="1998-01-01">5</MultiDeposits>
</BookingCostings>
and using the XSLT version 2.0 sheet:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:variable name="deposits" as="element(MultiDeposits)+">
<xsl:perform-sort select="BookingCostings/MultiDeposits">
<xsl:sort select="#depositDate"/>
</xsl:perform-sort>
</xsl:variable>
first date:<xsl:value-of select="$deposits[1]/#depositDate"/>,
last date:<xsl:value-of select="$deposits[last()]/#depositDate"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
the ouput will be:
first date:1998-01-01, last date:2010-08-09
Firstly, in your variable declaration, you do need to do something to create new nodes. Strictly speaking, you are not sorting them, but just reading through them in a given order. I think you need to add some sort of xsl:copy command.
<xsl:variable name="deposits">
<xsl:for-each select="/BookingCostings/MultiDeposits">
<xsl:sort select="substring(#DepositDate, 1, 4)" />
<xsl:sort select="substring(#DepositDate, 6, 2)" />
<xsl:sort select="substring(#DepositDate, 9, 2)" />
<xsl:copy-of select=".|#*" />
</xsl:for-each>
</xsl:variable>
What this creates is a 'node-set', but to access it you will need to make use of an extension function in XSLT. Which one you use depends on the XSLT processor you are using. In the example I am about to give, I am using the Microsoft one.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt" version="1.0">
Then, to access the nodes in your variable, you can do something like this
<xsl:value-of select="ms:node-set($deposits)/MultiDeposits[1]/#DepositDate" />
Here is a good article to read up on node-sets
Xml.com article on Node-Sets
Guess (don't have dev env to hand):
Add
<xsl:value-of select="." />
Before the closing </xsl:for-each>