Solved

XSLT

Posted on 2001-08-22
9
730 Views
Last Modified: 2013-11-18
I'm using XSLT to transform an XML document into a new XML document.  What I would like to know is if there is any way that I can perform some text manipulation on the contents of an element.  Fore example, let's say I've got the following XML element:

    <time>2001-07-23T19:41:07.788Z</time>

IN the output document I would like this to appear as:

    <time>July 23, 2001 at 7:41 PM</time>

While this is a date, what I'm asking is if applying a gnereic programmatic transform to element text is possible with XSLT?  If not it seems to me that this would be quite a significant shortcoming.  

Also, could someone give me an idea of a good way to do this while sticking with XML as my data source?  I guess I could do it with straight DOM manipulation in Java, or XPath and Java?  In which case I'd have to build the output document programatically.  Any thoughts?
0
Comment
Question by:derekl
9 Comments
 
LVL 4

Expert Comment

by:sdussinger
ID: 6415570
What you want to do can't be done by XSLT alone. XSLT is basically a structure-modification language, not a pattern-manipulation language. Primitives or functions for doing advanced string manipulation just don't exist in XSLT... :-(

That being said, it's pretty simple to add Java class functions as extension functions to XSLT.

The sample programs which come with the XALAN XSLT processor have an example which uses the Java SimpleDateFormatter class to do nearly what you want to do.

Check out the ${XALAN_HOME}/samples/Extensions/2java-namespace xml and xsl files to see how it works.

HTH

--Steve
0
 
LVL 4

Expert Comment

by:edmund_mitchell
ID: 6415948
Hello  derekl

What you want to do can be done with XSLT, but date parsing is going to get verbose.  Steve, you have got to quit being so pessimistic about XSLT :>)
It's a simple theory, but it has some sheer tonnage:

sample.xml:

<a>
 <time>2001-07-23T19:41:07.788Z</time>
 <time>2000-11-09T05:05:07.788Z</time>
 </a>

sample.xsl:

<?xml version='1.0' encoding='utf-8'?>
  <xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="months.uri">
 
 
  <months:month abbr="January" num="01"/>
  <months:month abbr="February" num="02"/>
  <months:month abbr="March" num="03"/>
  <months:month abbr="April" num="04"/>
  <months:month abbr="May" num="05"/>
  <months:month abbr="June" num="06"/>
  <months:month abbr="July" num="07"/>
  <months:month abbr="August" num="08"/>
  <months:month abbr="September" num="09"/>
  <months:month abbr="October" num="10"/>
  <months:month abbr="November" num="11"/>
  <months:month abbr="December" num="12"/>

  <!-- betcha haven't seen this trick before -->
  <xsl:variable name="the-months" select="document('')//months:month"
  />
  <!-- pretty cool, huh? ;>)  -->

  <xsl:template match="//time">
  <xsl:variable name='hour'>
       <xsl:choose>
          <xsl:when test="(substring(current(), 12, 2)) &lt; 12">
          <xsl:value-of select='substring(current(), 12, 2)'/>
       </xsl:when>
       <xsl:when test='substring(current(), 12, 2) = 12'>
           <xsl:value-of select='12'/>
       </xsl:when>
       <xsl:otherwise>
          <xsl:value-of select='(substring(current(), 12, 2)) - 12'/>
       </xsl:otherwise>
        </xsl:choose>
  </xsl:variable>
  <xsl:variable name='minute' select='substring(current(), 15, 2)'/>
  <xsl:variable name='am-pm'>
    <xsl:choose>
     <xsl:when test="substring(current(), 12, 2) > 11">
        PM
      </xsl:when>
      <xsl:otherwise>
        AM
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
 
  <xsl:element name='time'>
  <xsl:value-of select="concat($the-months[@num = substring(current(), 6,
  2)]/@abbr, ' ', substring(current(), 9, 2), ', ',
  substring(current(), 1, 4), ' at ', $hour, ':', $minute, $am-pm)"/>
  </xsl:element>  
 </xsl:template>
</xsl:stylesheet>

results in


ejm ->java com.icl.saxon.StyleSheet eexml.xml eexsl.xsl
<?xml version="1.0" encoding="utf-8"?>
 <time>July 23, 2001 at 7:41
        PM
      </time>
 <time>November 09, 2000 at 05:05
        AM
      </time>

Obviously, you'll want to pretty it up a bit, and maybe throw in another nested choose to get rid of the leading zeros in dates and am times.

One last point about XSLT:
It is a Turing-complete language, which means that according to Alan Turing, given data it can handle, XSLT can perform algorithms of arbitrary complexity - i.e., it can do almost anything.  Since it's a functional, not procedural, programming language, it can take a different mind-set to solve some problems, but that's what we're here to help you with :>)

I hope that answers your question,

Edmund
0
 
LVL 4

Expert Comment

by:sdussinger
ID: 6415969
Sorry Edmund. I'm not being pessimistic about XSL, but that script really makes my point.

There is no advanced mechanisms for doing string manipulation in XSL (and no I don't consider substring an advanced string manipulation mechanism :-)). So you've been forced to resort to something that while it works is going to be most difficult to modify/maintain. Ripping things with substrings works, but its tough to read/modify/maintain. I pity the poor guy who has to change the date formatting five years down the road :-)

The reference to Turing-completeness is accurate, but it belies the point: Granted that (given data it can handle) it can process algorithms of arbitrary complexity, but those algorithms are so difficult to create and read that it becomes nearly unusable.

The other point here is that of performance: I'd be willing to bet that doing all of that substring manipulation is going to cost you more than a single call to a Java class to do the same thing. Additionally, the code for doing the date conversion in Java will be much easier to understand and modify than the substring manipulation in an XSL file.

That being said, it is a rather cool approach, even if it is unwieldy... :-). I especially like the referencing of elements from the XSL file, I didn't know you could do that...

--Steve

0
 
LVL 4

Accepted Solution

by:
sdussinger earned 100 total points
ID: 6416057
Here is the code for the XSL/Java extension approach.

The XML (same as from Edmund's post):

<?xml version="1.0" encoding="utf-8"?>
<a>
   <time>2001-07-23T19:41:07.788Z</time>
   <time>2000-11-09T05:05:07.788Z</time>
</a>

The XSL Stylesheet:

<?xml version='1.0' encoding='utf-8'?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:java="http://xml.apache.org/xslt/java">
                 
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
  <root>
    <xsl:apply-templates/>
  </root>
</xsl:template>

<xsl:template match="//time">

  <time>
    <xsl:value-of select="java:DateFormatter.convertDate(string(.))"/>
  </time>

</xsl:template>

</xsl:stylesheet>

The Java code:

import java.text.*;
import java.util.*;

public class DateFormatter
{

  public static String convertDate (String sourceDate)
  {
    SimpleDateFormat sourceFormat = new SimpleDateFormat ("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
    SimpleDateFormat destFormat = new SimpleDateFormat ("MMMM dd, yyyy 'at' hh:mm a");

    Date d = sourceFormat.parse (sourceDate, new ParsePosition (0));
    return (destFormat.format (d));
  }

}


Now when this is run, you get:

<root>
   <time>July 23, 2001 at 07:41 PM</time>
   <time>November 09, 2000 at 05:05 AM</time>
</root>

BTW, this was run using Xerces/Xalan.

--Steve
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 27

Expert Comment

by:BigRat
ID: 6419602
To be fair Steve, XALAN is a server-side Java processor. Not everybody uses this technology. A lot of people still use ASP and they don't have <xsl:eval/> any more, since the purists said it was non-standard. Furthermore I don't remember seeing value-of="java:..." in the XML standard!

I take my hat off to Edmund, I just haven't the time to spend on such problems. There is a futher point about this, which the designers and the purists forget, and that is the productivity factor. It costs a lot of money to figure something like that out!

It really is a pity that xsl:eval went. The inclusion of Javascript would have been really super. I would have even supported REGEX or any other pattern language to be able to manipulate the data instead of just being able to manipulate format.
0
 
LVL 4

Expert Comment

by:sdussinger
ID: 6420261
Sure and I understand that not all people are using server-side XML and I certainly would not propose that anyone change the way that they are doing things just so that they adopt a technology which I consider to be useful.

Perhaps using Java extensions isn't in the standard, but nonetheless it is a way to do what was requested.  Since no reference was made to the way in which XSL was being used, and the original post made reference to Java as a technology which was a potential solution to the problem, I took off from that point with my post.  I believe, though I could be wrong, that the value-of="java:" is a take off on the idea of extension functions which is part of the specification.

I have nothing against the idea of using XSL to provide this behavior, but I also believe that my points are still valid.  Especially, as you point out that the productivity factor associated with trying to develop a solution using raw XSL is time consuming and potentially error prone.

I'm certainly, as you can tell, not purist when it comes to technology. Any port in a storm and all that; I'll use whatever's available. I'm certainly not judging anyone's approach here, but I think its important to explore all avenues.  If the XSL-only approach is what the original poster wants, then that's what he should use. I posted mine as an alternative which could also be used and which I feel has merits over the other in certain situations.

It may not sound like it, but I think the idea that Edmund proposed was pretty cool... I'm upset that I didn't think of it, although I would only use it as an academic exercise. I wouldn't want to put it into production, for fear of someone who didn't understand it bunging it up while making changes.

I certainly didn't want to get anyone upset over my posting, and if that's the case I apologize. I merely wanted the original poster to have the benefit of several different approaches to try.

--Steve
0
 
LVL 2

Expert Comment

by:Deathwish
ID: 6421964
Instead of eval, if you are using the MSXML parser you can tie scripting directly into the xslt. For example here is one I did with date formatting. In this case my date was something like 1/1/01 and what was wanted was to show the fule date. It uses vbscript in this case. It can be modified to accommodate what you want and the format you want. Whats cool about is that you can use any scripting language that the server supports. You can even do com components and such within the script blocks for more advance calculations.


The template to output it:

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:msxsl="urn:schemas-microsoft-com:xslt"
     xmlns:dt="urn:schemas-microsoft-com:datatypes"
     xmlns:date="urn:schemas-irp-com:date" version="1.0">
     <xsl:output method="html"/>
     
     <xsl:template name="test">
     <td><xsl:value-of select="date:FormatDate(string(@PlanEffectiveDate))"/></td>
     <td>Requestor:</td>
     <td><xsl:value-of select="@Requestor"/></td>
                   </tr>
     </xsl:template>
</xsl:stylesheet>





The script template:


<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:date="urn:schemas-irp-com:date"
                version="1.0">
  <msxsl:script language="VBScript" implements-prefix="date">
    Function FormatDate(datDateItem)
          FormatDate = Right("0" &amp; Month(datDateItem),2) &amp; "/" &amp; Right("0" &amp; Day(datDateItem),2) &amp; "/" &amp; Year(datDateItem)
     End Function
  </msxsl:script>

</xsl:stylesheet>
0
 
LVL 2

Expert Comment

by:Deathwish
ID: 6421971
Oops I meant they wanted to show the full year at the end.
0
 

Author Comment

by:derekl
ID: 6422062
Thanks for all of the comments guys they were very insightful.  Edmund I have to say your solution to the problem was exceptionally clever, but Steve's answer was more of what I had in mind when I asked the question.  Again, thanks for the insight!
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Suggested Solutions

Most of the sites are being standardized with W3C Web Standards. W3C provides lot of web standard services to the web. They have the web specification, process and documentation for all the web standards. You can apply HTML, CSS and Accessibility st…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…
The viewer will learn the benefit of using external CSS files and the relationship between class and ID selectors. Create your external css file by saving it as style.css then set up your style tags: (CODE) Reference the nav tag and set your prop…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now