Kevin Anderson
Kevin Anderson

Reputation: 79

Format a date (string) with XSLT

There are a lot of questions/answers on this particular subject, but none seem to address the specific issue I have. I'm also a complete noob, so there's that ;-). I have the following XML being sent from an application to a piece of middleware. In that middleware, I'm capturing the XML and pulling out data. My last remaining challenge is to re-format a piece of string data that represents a date, that sits inside the <now></now> tags.

<?xml version="1.0" encoding="UTF-8"?>
    <Body>
        <send_message xmlns="urn:xyztech:agent">
            <user>
                <now>2017-11-15T13:38:11+00:00</now>
                <company>xyz.demo</company>
            </user>
        </send_message>
    </Body>

I'm using Apigee for the middleware, and need an XSL script to manipulate the date (to make it a bit cleaner - something like 15/11/2017 or 15.11.2017, and I don't want the time). I've tried several examples that I've seen, but none seem to work for me. Unfortunately, being the noob, I'm not really able to debug the errors that occur with those examples (e.g. failing to find the function from the namespace reference), and I'd really appreciate some assistance/guidance.

It would be pretty slick if I could use the XPath that I already have in the XSL that extracts the data:

<Variable name="now" type="date">
    <XPath>//Envelope/Body/send_message/user/now</XPath>
</Variable>

I've seen a solution for that, but I have a feeling that Apigee doesn't support XPath 2.0, so format-date($d,"[D]-[M]-[Y]") doesn't apper to be a viable option, either. Any/all help gratefully received...

Upvotes: 1

Views: 10492

Answers (3)

Valdi_Bo
Valdi_Bo

Reputation: 30971

"I have a feeling" is not a proper approach to the issue. Just check whether Apigee "understands" XSLT 2.0.

If it does, use format-date function. Note the way the format is given in the following example. It provides proper size of day / month / year part.

Another important detail: Your source XML uses a namespace and the script refers to an elements from it (now). So:

  • This namespace must be included in stylesheet tag, with a namespace reference (I used xyz).
  • The now element must be given with this namespace reference.

So the whole script can be as follows:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xyz="urn:xyztech:agent">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="xyz:now">
    <xsl:copy>
      <xsl:variable name="srcDate" select="substring-before(., 'T')" />
      <xsl:value-of select="format-date(xs:date($srcDate),'[D01].[M01].[Y0001]')"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node()|@*">
    <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
  </xsl:template>
</xsl:stylesheet>

I tested it on your source using http://xsltransform.net/ and it works.

Of course, the simplest way to check is to try this script in your environment.

Upvotes: 2

Kevin Anderson
Kevin Anderson

Reputation: 79

The answer (full solution), taking some of what Christian Mosz & valdi_bo suggested above, and a final wonderfully helpful solution and explanation from @friedemann_bach (from a separate question I asked) is this, as the full XSL I needed:

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ms="urn:schemas-microsoft-com:xslt">

<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="now">
  <now>
    <xsl:variable name="srcDateTime" select="//Envelope/Body/send_message/user/now"/>
    <xsl:variable name="srcDate" select="substring-before($srcDateTime, 'T')" />
    <xsl:variable name="outDate" select="format-date(xs:date($srcDate),'[D01].[M01].[Y0001]')"/>
    <xsl:value-of select="$outDate" disable-output-escaping="yes"/>
  </now>
</xsl:template>
</xsl:stylesheet>

Whilst the first two answers here provided the solution to the date format, the issue I then had to contend with was not understanding why all the other tags were being ripped out. The first (additional) template fixes that (it serves to change nothing, so that the second template overrides "no change" only on the node I select in the second template. Many thanks to everyone that helped. Being a noob (learning through simply HAVING to do it & get it done), I needed the full solution AND the explanation. I know it's a little "please do it for me", but it all adds to the knowledge, and that's kinda the point. Thanks all again.

Upvotes: 0

Christian Mosz
Christian Mosz

Reputation: 543

How about this:

<xsl:variable name="onlydate" select="substring-before('2017-11-15T13:38:11+00:00', 'T')"/>
<xsl:variable name="tokens" select="tokenize($onlydate, '-')"/>
<xsl:value-of select="concat($tokens[3], '.', $tokens[2], '.', $tokens[1])"/>

This worked for me and i got the output '15.11.2017'.

  • Substring-before returns you the string before your specified string occours.
  • Tokenize splits the argument into an array on the given character.

Upvotes: 1

Related Questions