Sunday, March 27, 2011

XSLT transformation datetime to date format

I'm trying to transform a datetime to a date format yyyy-MM-dd, because I'm using the xsd.exe tool the xs:date datatypes are automatically changed into a datetime datatype, because there is no type in the .NET Framework that matches the type xs:date completely.

But I can't get it to work

<articles>
        <article>
          <articleid>48992</articleid>
          <deliverydateasked>2009-01-29T00:00:00+01:00</deliverydateasked>
        </article>
        <article>
          <articleid>48993</articleid>
          <deliverydateasked>2009-01-30T00:00:00+01:00</deliverydateasked>
        </article>
</articles>

trying to convert the xml to

<articles>
        <article>
          <articleid>48992</articleid>
          <deliverydateasked>2009-01-29</deliverydateasked>
        </article>
        <article>
          <articleid>48993</articleid>
          <deliverydateasked>2009-01-30</deliverydateasked>
        </article>
</articles>

currently I'm using this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
 <articles>
  <xsl:apply-templates select="article">
  </xsl:apply-templates>
            </articles>
</xsl:template>

<xsl:template name="FormatDate">

 <xsl:param name="DateTime" />
 <xsl:variable name="date">
  <xsl:value-of select="substring-before($DateTime,'T')" />
 </xsl:variable>

 <xsl:if test="string-length($date) != 10">
  <xsl:value-of select="$DateTime"/>
 </xsl:if>
 <xsl:if test="string-length($date) = 10">
  <xsl:value-of select="$date"/>
 </xsl:if>
</xsl:template>

<xsl:template match="article">
  <xsl:call-template name="FormatDate">
   <xsl:with-param name="DateTime" select="deliverydateasked"/>
  </xsl:call-template> 
</xsl:template>

Does anyone know a good xslt transformation.

Thanks in advance

The output result of my code is

<articles />
From stackoverflow
  • Frankly, this looks about right to me - sometimes a simple substring is good enough.

    However, if you're in .NET land and you're really needing extra functionality .NET has XSLT Extension Objects


    edit: oic, you've got a basic apply-templates conceptual problem. Try this (note the copy and the root template match):

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
    <xsl:template match="*">
        <xsl:copy><xsl:apply-templates /></xsl:copy>
    </xsl:template>
    
    <xsl:template match="deliverydateasked">
        <xsl:copy>
            <xsl:call-template name="FormatDate">
                <xsl:with-param name="DateTime" select="."/>
            </xsl:call-template>    
        </xsl:copy>
    </xsl:template>
    
    <xsl:template name="FormatDate">
    
            <xsl:param name="DateTime" />
            <xsl:variable name="date">
                    <xsl:value-of select="substring-before($DateTime,'T')" />
            </xsl:variable>
    
            <xsl:if test="string-length($date) != 10">
                    <xsl:value-of select="$DateTime"/>
            </xsl:if>
            <xsl:if test="string-length($date) = 10">
                    <xsl:value-of select="$date"/>
            </xsl:if>
    </xsl:template>
    
    </xsl:stylesheet>
    

    templates is a hard concept to learn, you might be better off starting with the more straightforward for-each, and/or it seems you could do with some XSLT tutorials/books.

    Tomalak : On "using the more straightforward for-each": http://gregbeech.com/blogs/tech/archive/2006/08/17/using-xsl-for-each-is-almost-always-wrong.aspx. I vote for doing it properly from the start. :)
    annakata : From personal experience of teaching XSLT to newbies I strongly disagree with that - for-each is a translatable and easily grasped concept, and if anything is a handy springboard to explain the template concept "now you've got that, see what this can do!"
    Val : The link Tomalak gave to the Greg Beech article has changed; it's now http://gregbeech.com/blog/using-xsl-for-each-is-almost-always-wrong
  • Thanks to Stesoc and annakata I figured it out This is the code I'm now using and it works perfect

    <xsl:template match="*">
     <xsl:param name="parentElm">
      <xsl:value-of select="name(..)" />
     </xsl:param>
     <xsl:choose>
      <xsl:when test="local-name() = 'deliverydateasked'">
       <xsl:element name="deliverydateasked">
        <xsl:call-template name="FormatDate">
         <xsl:with-param name="DateTime" select="."/>
        </xsl:call-template>
       </xsl:element>
      </xsl:when>
      <xsl:otherwise>
       <xsl:element name="{local-name()}">
        <xsl:copy-of select="@*" />
        <xsl:apply-templates select="@* | node()" />
       </xsl:element>
      </xsl:otherwise>
     </xsl:choose>
    </xsl:template>
    
    <xsl:template name="FormatDate">
     <xsl:param name="DateTime" />
     <xsl:variable name="date">
      <xsl:value-of select="substring-before($DateTime,'T')" />
     </xsl:variable>
    
     <xsl:if test="string-length($date) != 10">
      <xsl:value-of select="$DateTime"/>
     </xsl:if>
     <xsl:if test="string-length($date) = 10">
      <xsl:value-of select="$date"/>
     </xsl:if>
    </xsl:template>
    

    Tomalak : Why do you do the complicated "local-name()" stuff?
    annakata : what tomalak said - leverage xsl:copy
    freggel : Annakata I have tried your first suggestion with result: 2009-01-292009-01-30 Or did I something wrong? but is local-name() than a function to avoid? the duration time is now 15-30 ms
  • Formatting will get a lot easier in XPath 2.0, which Microsoft has currently refused to support for the last 8 years. Since the formatting issue is really only persistent for XSLT in .Net I like to use a custom function, which is cleaner & easier:

    XSLT With Formatting Function:

        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:msxsl="urn:schemas-microsoft-com:xslt"
        xmlns:user="http://www.tempuri.org/User">
    
      <msxsl:script implements-prefix="user" language="C#">
            <![CDATA[
              public string FormatCurrency(string amount)
              {
                return decimal.Parse(amount).ToString("C0");
              }
    
              public string FormatDate(string dateValue)
              {
                return DateTime.Parse(dateValue).ToString("MM/dd/yyyy hh:mm");
              }
              ]]>
          </msxsl:script>
    

    Usage:

    <xsl:value-of select="user:FormatDate(@transactionDate)"/>
    <xsl:value-of select="user:FormatCurrency(@amount)"/>
    

    When you execute your XSLT in .Net make sure to tell it that it's trusted (so that the msxsl:script block will run.

    XslCompiledTransform.Load(reader, XsltSettings.TrustedXslt, null);
    

0 comments:

Post a Comment