I want to sort out the values of "number" by the Position/or first come in the XML document of the T.number values and display it. Is there a way to do this?
Here is my XML document
<?xml version="1.0" encoding="UTF-8"?>
<JobList>
<Job ID="2" />
<Job ID="3" />
<Job ID="5" />
<Job ID="4" />
<Job ID="6" />
<Job T.number="28" />
<Job T.number="10" />
<Job T.number="24" />
<Job T.number="75" />
<Job T.number="75" />
<Tool number="10" />
<Tool number="24" />
<Tool number="28" />
<Tool number="75" />
<Tool number="75" />
</JobList>
Desired Result:
<?xml version="1.0" encoding="UTF-8"?>
<JobList>
<Job ID="2" />
<Job ID="3" />
<Job ID="5" />
<Job ID="4" />
<Job ID="6" />
<Tool number="28" />
<Tool number="10" />
<Tool number="24" />
<Tool number="75" />
</JobList>
Here is the XSL document:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" encoding="UTF-8" method="xml" />
<xsl:param name="REPORT">joblist</xsl:param>
<xsl:param name="LOCALE">en-US</xsl:param>
<xsl:param name="FORMAT">html</xsl:param>
<xsl:param name="CAPTURE">example,job</xsl:param>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>Joblist Report</title>
<style type="text/css">
body {font-family: Arial;}
</style>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="JobList">
<div>
<table width="100" border="1">
<thead>
<tr>
<td>
<xsl:value-of select="Sorted Numbers" />
</td>
</tr>
</thead>
<tbody>
<xsl:variable name="vsortOrder" select="//Job[#T.number]" />
<xsl:for-each select="Tool">
<xsl:sort select="#number" order="{$vsortOrder}" data-type="number" />
<tr>
<td>
<xsl:value-of select="#number" />
</td>
</tr>
</xsl:for-each>
</tbody>
</table>
</div>
</xsl:template>
</xsl:stylesheet>
UPDATE: I have found an attribute that has the same number order I need. I updated the XML with the attribute. But the XSL still doesnt want to sort it out.
Your desired output looks a lot different than what your actual code would produce. But maybe this will help you. This code will get your desired output. Then you can modify it to be what you actually want.
<!-- Change your for-each loop. -->
<xsl:for-each select="$vsortOrder/#T.number">
<xsl:variable name="tnumber" select="."/>
<xsl:copy-of select="//Tool[#number=$tnumber]"/>
</xsl:for-each>
Or, you could modify your code to do this. Since you are only printing out the number, you do not have to lookup the tool.
<!-- Change your for-each loop. -->
<xsl:for-each select="$vsortOrder/#T.number">
<tr>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:for-each>
Related
Here is a snippet of XML:
<?xml version="1.0" encoding="utf-8"?>
<AssignmentHistory Version="171804">
<W20160229>
<ReviewQuestion>Why will God’s Kingdom have to crush the earthly rulerships depicted in the image? (Da 2:44)</ReviewQuestion>
<StudentItems>
<Item>
<Name Counsel="9" NextCounsel="0" Completed="1">Finlay Truckle</Name>
<Type>Bible Reading (Main)</Type>
<Description>Bible Reading</Description>
</Item>
<Item>
<Name Counsel="38" NextCounsel="0" Completed="1">Name</Name>
<Type>#1 Student (Main)</Type>
<Description>Initial Call</Description>
</Item>
<Item>
<Name>Name</Name>
<Type>Assistant</Type>
<Description>Initial Call</Description>
</Item>
<Item>
<Name Counsel="41" NextCounsel="0" Completed="1">Name</Name>
<Type>#2 Student (Main)</Type>
<Description>Return Visit</Description>
</Item>
<Item>
<Name>¬DELETED¬</Name>
<Type>Assistant</Type>
<Description>Return Visit</Description>
</Item>
<Item>
<Name Counsel="45" NextCounsel="0" Completed="1">Name</Name>
<Type>#3 Student (Main)</Type>
<Description>Bible Study</Description>
</Item>
<Item>
<Name>Name</Name>
<Type>Assistant</Type>
<Description>Bible Study</Description>
</Item>
</StudentItems>
</W20160229>
<W20160404/>
<W20160411>
<ReviewQuestion>What did the immense tree in Nebuchadnezzar’s dream represent? (Da 4:10, 11, 20-22)</ReviewQuestion>
<StudentItems>
<Item>
<Name Counsel="11" NextCounsel="0" Completed="1">Name</Name>
<Type>Bible Reading (Main)</Type>
<Description>Bible Reading</Description>
</Item>
<Item>
<Name Counsel="0" NextCounsel="0" Completed="1">Name</Name>
<Type>#1 Student (Main)</Type>
<Description>Initial Call</Description>
</Item>
<Item>
<Name>Name</Name>
<Type>Assistant</Type>
<Description>Initial Call</Description>
</Item>
<Item>
<Name Counsel="37" NextCounsel="0" Completed="1">Name</Name>
<Type>#2 Student (Main)</Type>
<Description>Return Visit</Description>
</Item>
<Item>
<Name>Name</Name>
<Type>Assistant</Type>
<Description>Return Visit</Description>
</Item>
<Item>
<Name Counsel="0" NextCounsel="0" Completed="1">Name</Name>
<Type>#3 Student (Main)</Type>
<Description>Bible Study</Description>
</Item>
<Item>
<Name>Name</Name>
<Type>Assistant</Type>
<Description>Bible Study</Description>
</Item>
</StudentItems>
</W20160411>
</AssignmentHistory>
Now, in my XSL script I link into the above document like this (just a snippet for now):
<tr>
<td class="cellComments" colspan="4">
<xsl:variable name="AssignHistory" select="document('AssignHistory.xml')"/>
<xsl:variable name="week" select="Date/#NextWeek"/>
<xsl:variable name="NextReviewQuestion" select="$AssignHistory/AssignmentHistory/*[name()=$week]/ReviewQuestion"/>
<xsl:if test="normalize-space($NextReviewQuestion) != ''">
<span class="textReviewQuestionLabel">
<xsl:value-of select="//Labels/NextReviewQuestion"/>
</span>
<span class="textReviewQuestion">
<xsl:value-of select="$NextReviewQuestion"/>
</span>
<br />
</xsl:if>
<br />
<br />
<br />
<span style="font-size: 8pt;">
<xsl:apply-templates select="$AssignHistory/AssignmentHistory/*[name()=$week]/StudentItems">
<xsl:with-param name="MainHall" select="//Labels/MainHall"/>
<xsl:with-param name="AuxClass1" select="//Labels/AuxClass1"/>
<xsl:with-param name="AuxClass2" select="//Labels/AuxClass2"/>
</xsl:apply-templates>
</span>
</td>
</tr>
Now this is the issue. See the week: <W20160404/>? This week was a special event. We didn't have a meeting because we all went somewhere else for a assembly. As a result, there is no details.
So this is what I want to do:
If the week that I am trying to get details from is empty (special event) attempt to get the value from the next sibling instead (if there is one). Otherwise, if it is not empty (a normal meeting) just use the returned value like I do now.
It does get a bit more complicated for the students bit of script. But the principle is the same. To use the following sibling if required.
I am sure I have worded this much more complicated than it needs to be.
Update
I will reform the question in due course.
Here is a history snippet:
<?xml version="1.0" encoding="utf-8"?>
<AssignmentHistory Version="171804">
<W20160229>
<ReviewQuestion>Why will God’s Kingdom have to crush the earthly rulerships depicted in the image? (Da 2:44)</ReviewQuestion>
</W20160229>
<W20160404/>
<W20160411>
<ReviewQuestion>What did the immense tree in Nebuchadnezzar’s dream represent? (Da 4:10, 11, 20-22)</ReviewQuestion>
</W20160411>
<W20170803>
<ReviewQuestion>Test question</ReviewQuestion>
</W20170803>
</AssignmentHistory>
Here is the main XML snippet:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="TestTransform.xsl"?>
<MeetingWorkBook>
<Meeting>
<Date ThisWeek="W20160229" NextWeek="W20160404">Date 1</Date>
</Meeting>
<Meeting>
<Date ThisWeek="W20160404" NextWeek="W20160411">Date 2</Date>
</Meeting>
<Meeting>
<Date ThisWeek="W20160411" NextWeek="W20170803">Date 3</Date>
</Meeting>
</MeetingWorkBook>
Here is the simplified XSL snippet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" version="4.01"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
doctype-public="//W3C//DTD XHTML 1.0 Transitional//EN"/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
</head>
<body>
<table>
<xsl:for-each select="MeetingWorkBook/Meeting">
<tr>
<td>
<xsl:value-of select="Date/#ThisWeek"/>
</td>
<td>
<xsl:value-of select="Date/#NextWeek"/>
</td>
<td>
<xsl:variable name="AssignHistory" select="document('TestHist.xml')"/>
<xsl:variable name="week" select="Date/#NextWeek"/>
<xsl:variable name="NextReviewQuestion" select="$AssignHistory/AssignmentHistory/*[name()=$week]/ReviewQuestion"/>
<xsl:if test="normalize-space($NextReviewQuestion) != ''">
<xsl:text>Question: </xsl:text>
<xsl:value-of select="$NextReviewQuestion"/>
</xsl:if>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Now, if you view the main XML file in IE you will get:
W20160229 W20160404
W20160404 W20160411 Question: What did the immense tree in Nebuchadnezzar’s dream represent? (Da 4:10, 11, 20-22)
W20160411 W20170803 Question: Test question
This shows the problem. The dates are fictional. So this is the context where I want to use the following sibling "ReviewQuestion" if the first attempt returns an empty node.
Update 2
Based on your excellent revision to your answer I was able to make the simpler bit of my script work as expected. I broke down the code a bit more as I needed a conditional label prefix "Question: ". So at the moment I have:
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
</head>
<body>
<table>
<xsl:for-each select="MeetingWorkBook/Meeting">
<tr>
<td>
<xsl:value-of select="Date/#ThisWeek"/>
</td>
<td>
<xsl:value-of select="Date/#NextWeek"/>
</td>
<td>
<xsl:variable name="AssignHistory" select="document('TestHist.xml')"/>
<xsl:variable name="week" select="Date/#NextWeek"/>
<xsl:variable name="history-week" select="$AssignHistory/AssignmentHistory/*[name()=$week]"/>
<xsl:variable name="NextReviewQuestion" select="($history-week | $history-week/following-sibling::*)/ReviewQuestion"/>
<xsl:if test="normalize-space($NextReviewQuestion) != ''">
<xsl:text>Question: </xsl:text>
<xsl:value-of select="$NextReviewQuestion"/>
</xsl:if>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
My only remaining bit to address now is that I originally also had this code:
<xsl:apply-templates select="$AssignHistory/AssignmentHistory/*[name()=$week]/StudentItems">
<xsl:with-param name="MainHall" select="//Labels/MainHall"/>
<xsl:with-param name="AuxClass1" select="//Labels/AuxClass1"/>
<xsl:with-param name="AuxClass2" select="//Labels/AuxClass2"/>
</xsl:apply-templates>
I need to do the same thing again now. We need to use the following sibling "StudentItems" if the first one returned nothing.
Then we are done.
Consider the following simplified example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="AssignmentHistory">
<table border="1">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'W')]">
<tr>
<td>
<xsl:value-of select="name()" />
</td>
<xsl:choose>
<xsl:when test="*">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::*[*][1]/*"/>
</xsl:otherwise>
</xsl:choose>
</tr>
</xsl:template>
<xsl:template match="ReviewQuestion">
<td>
<xsl:value-of select="." />
</td>
</xsl:template>
<xsl:template match="StudentItems">
<!-- ??? -->
</xsl:template>
</xsl:stylesheet>
When applied to the given XML example, the result (rendered) will be:
Added:
Regarding the problem presented in the Update part of your question:
Try replacing this part:
<td>
<xsl:variable name="AssignHistory" select="document('TestHist.xml')"/>
<xsl:variable name="week" select="Date/#NextWeek"/>
<xsl:variable name="NextReviewQuestion" select="$AssignHistory/AssignmentHistory/*[name()=$week]/ReviewQuestion"/>
<xsl:if test="normalize-space($NextReviewQuestion) != ''">
<xsl:text>Question: </xsl:text>
<xsl:value-of select="$NextReviewQuestion"/>
</xsl:if>
</td>
with:
<td>
<xsl:variable name="AssignHistory" select="document('TestHist.xml')"/>
<xsl:variable name="week" select="Date/#NextWeek"/>
<xsl:variable name="history-week" select="$AssignHistory/AssignmentHistory/*[name()=$week]"/>
<xsl:value-of select="($history-week | $history-week/following-sibling::*)[normalize-space(ReviewQuestion)][1]/ReviewQuestion"/>
</td>
I have a generic template I've designed with 2 params title and category.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:param name="category" />
<xsl:param name="title" />
<xsl:template name="test-group">
<fo:block>
<xsl:value=of select="$title" />
</fo:block>
<fo:block>
<xsl:value-of select="$category" />
</fo:block>
</xsl:template>
</xsl:stylesheet>
In the parent template I have the following:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:include href="templates/generic_template.xsl" />
...
<xsl:call-template name="test-group">
<xsl:with-param name="category" select="'animals'" />
<xsl:with-param name="title" select="'DOGS'" />
</xsl:call-template>
...
</xsl:stylesheet>
However, when the transform completes title and category are blank. I'm using FOP 2.0 so I'm not sure if this is a known shortcoming.
When defining a xsl:template that takes parameters, the parameter names used within the xsl:template should be declared using xls:param elements nested within.
<xsl:template name="test-group">
<xsl:param name="category" />
<xsl:param name="title" />
...
</xsl:template>
The parameters being attached to the xsl:template and not the xsl:stylesheet.
This is similar to when calling the template with xsl:call-template, except you are specifying the values instead using xsl:with-param.
I have two xslt transformations to apply to an xml message.
<?xml version="1.0" encoding="UTF-8"?>
<ListOfBipBoxfldrlbls>
<Batch>
<ListOfFolder>
<Folder>
<FolderNum>Fldr1</FolderNum>
<BoxNumber>Box1</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
<Folder>
<FolderNum>Fldr2</FolderNum>
<BoxNumber>Box1</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
<Folder>
<FolderNum>Fldr3</FolderNum>
<BoxNumber>Box1</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
<Folder>
<FolderNum>Fldr1</FolderNum>
<BoxNumber>Box2</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
<Folder>
<FolderNum>Fldr2</FolderNum>
<BoxNumber>Box2</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
<Folder>
<FolderNum>Fldr3</FolderNum>
<BoxNumber>Box2</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
<Folder>
<FolderNum>Fldr4</FolderNum>
<BoxNumber>Box2</BoxNumber>
<BatchNumber>Batch</BatchNumber>
</Folder>
</ListOfFolder>
<ListOfBox>
<Box>
<BatchNumber>Batch</BatchNumber>
<BoxNumber>Box1</BoxNumber>
</Box>
<Box>
<BatchNumber>Batch</BatchNumber>
<BoxNumber>Box2</BoxNumber>
</Box>
</ListOfBox>
</Batch>
</ListOfBipBoxfldrlbls>
Expected Output :
Box1 Box1 Fldr1 Box1 Fldr2
Box1 Fldr3 Box2 Box2 Fldr1
Box2 Fldr2 Box2 Fldr3 Box2 Fldr4
Here is my xsl
<?xml version="1.0"?>
<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="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="phase-1-result">
<xsl:apply-templates select="/" mode="phase-1"/>
</xsl:variable>
<xsl:apply-templates select="$phase-1-result" mode="phase-2"/>
</xsl:template>
<xsl:template match="/" mode="phase-1">
<ListofLabels>
<xsl:for-each select="ListOfBipBoxfldrlbls/Batch/ListOfFolder/Folder">
<Label>
<FolderNum><xsl:value-of select="FolderNum"/></FolderNum>
<Box><xsl:value-of select="BoxNumber"/></Box>
<Batch><xsl:value-of select="BatchNumber"/></Batch>
</Label>
</xsl:for-each>
<xsl:for-each select="ListOfBipBoxfldrlbls/Batch/ListOfBox/Box">
<Label>
<Box><xsl:value-of select="BoxNumber"/></Box>
<Batch><xsl:value-of select="BatchNumber"/></Batch>
</Label>
</xsl:for-each>
</ListofLabels>
</xsl:template>
<xsl:template match="$phase-1-result/ListofLabels/Label" mode="phase-2">
<xsl:variable name="columns" select="3" />
<TABLE border="1">
<xsl:for-each select="$phase-1-result/ListofLabels/Label[position() mod $columns = 1]">
<TR>
<xsl:for-each select=".|following-sibling::$phase-1-result/ListofLabels/Label[position() < $columns]">
<TD>
<xsl:value-of select="." />
</TD>
</xsl:for-each>
</TR>
</xsl:for-each>
</TABLE>
</xsl:template>
</xsl:stylesheet>
I am trying to restructure the XML in the first pass and store the result in a variable "$phase-1-result" and format in the second pass using the new structure.
The problem is xmlspy is not recognizing the Variable. it is show it as undefined variable and Error: Unexpected token "$phase-1-result/ListofLabels/Label".
Can some one help me identify the Issue.
Thanks in advance.
You only need to use the variable in the initial <xsl:apply-templates select="$phase-1-result" mode="phase-2" />. After that you're "inside" the phase 1 result tree, and the match expressions and further selects don't need to use the variable, they just work within this new context:
<xsl:template match="/">
<xsl:variable name="phase-1-result">
<xsl:apply-templates select="/" mode="phase-1"/>
</xsl:variable>
<TABLE border="1">
<xsl:apply-templates mode="phase-2"
select="($phase-1-result/ListOfLabels/Label)[position() mod 3 = 1]"/>
</TABLE>
</xsl:template>
<!-- phase-1 template as before -->
<xsl:template match="Label" mode="phase-2">
<TR>
<xsl:apply-templates select=".|following-sibling::Label[position() lt 3]"
mode="columns" />
</TR>
</xsl:template>
<xsl:template match="Label" mode="columns">
<TD>
<xsl:value-of select="." />
</TD>
</xsl:template>
Here I'm doing the "select every third Label" logic at the point of applying the phase-2 template, so that template only needs to concern itself with the "me and my next two siblings" bit.
It's no different from declaring a variable containing nodes from the original input tree and then applying templates to those
<xsl:variable name="someNodes" select="/foo/bar | /foo/ping" />
<xsl:apply-templates select="$someNodes" />
<xsl:template match="bar">...</xsl:template>
The template match expressions don't care where the nodes came from, they only care what the nodes look like (is it a bar or a ping).
I am having two xml files. I want to copy one xml file data into another file under last occurrence of element. Below are the xml I am having :
---------- xml-1---------------
<?xml version="1.0"?>
<parent>
....
<form id="1" name="world"/>
<source id="1" name="abc1"/>
<source id="2" name="abc2"/>
<source id="3" name="abc3"/>
<file id="1" name="xyz"/>
....
</parent>
----------- xml-2--------------
<?xml version="1.0"?>
<root>
<source id="4" data="anything"/>
<source id="5" data="anything"/>
<source id="6" data="anything"/>
<source id="7" data="anything"/>
</root>
------------------The desired output I want-----------------
<?xml version="1.0"?>
<parent>
....
<source id="1" name="abc1"/>
<source id="2" name="abc2"/>
<source id="3" name="abc3"/>
<source id="4" data="anything"/>
<source id="5" data="anything"/>
<source id="6" data="anything"/>
<source id="7" data="anything"/>
<file id="1" name="xyz"
....
</parent>
============== xslt I am using =============
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="source">
<xsl:choose>
<xsl:when test="'id=3'">
<xsl:call-template name="identity"/>
<xsl:copy-of select="document('file:///D:/Softwares/JEE eclipse/JEEworkspace/Migration/TestMigrationWithoutDeletingProject/xml2.xml')/*/*"/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I am trying to find it out on the basis of id attribute but it is copying xml-2 data after every source element of xml-1 data. Please help me on this.
I think there are two small problems in your code:
The call to <xsl:call-template name="identity"/> should be outside (or rather before) your choose section. By the way: since you not have an otherwise section it would be slightly shorter to use xsl:if here.
The test for the correct insertion point should be test="#id = 3" since the term "'id=3'" is simply a string which always yields true().
I'm relatively new to XSL and am attempting to elegantly transform a Google Calendar feed into something more readable.
I would appreciate your eyes on whether there are optimizations to be made. In particular, I would like your advice on template use. I've read a lot about how for-each is not appropriate to use willy-nilly (rather, one should attempt to make judicious use of templates).
Thank you very much.
Original XML (showing only one event):
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gCal='http://schemas.google.com/gCal/2005' xmlns:gd='http://schemas.google.com/g/2005'>
<id>http://www.google.com/calendar/feeds/bachya1208%40gmail.com/public/full</id>
<updated>2011-09-19T21:32:50.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'/>
<title type='text'>John Doe</title>
<subtitle type='text'>John Doe</subtitle>
<link rel='alternate' type='text/html' href='https://www.google.com/calendar/embed?src=bachya1208#gmail.com'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full'/>
<link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/batch'/>
<link rel='self' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full?max-results=25'/>
<link rel='next' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full?start-index=26&max-results=25'/>
<author>
<name>John Doe</name>
<email>johndoe#gmail.com</email>
</author>
<generator version='1.0' uri='http://www.google.com/calendar'>Google Calendar</generator>
<openSearch:totalResults>1334</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<gCal:timezone value='America/Denver'/>
<gCal:timesCleaned value='0'/>
<entry>
<id>http://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/lp0upnpndnkp0ruqht7ef84kds</id>
<published>2011-09-14T21:15:16.000Z</published>
<updated>2011-09-14T21:15:16.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'/>
<title type='text'>Oil Change</title>
<content type='text'/>
<link rel='alternate' type='text/html' href='https://www.google.com/calendar/event?eid=bHAwdXBucG5kbmtwMHJ1cWh0N2VmODRrZHMgYmFjaHlhMTIwOEBt' title='alternate'/>
<link rel='self' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/lp0upnpndnkp0ruqht7ef84kds'/>
<author>
<name>John Doe</name>
<email>johndoe#gmail.com</email>
</author>
<gd:comments>
<gd:feedLink href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/lp0upnpndnkp0ruqht7ef84kds/comments'/>
</gd:comments>
<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'/>
<gd:where valueString='9955 E Arapahoe Road, Englewood, CO 80112 (Go Subaru Arapahoe)'/>
<gd:who email='johndoe#gmail.com' rel='http://schemas.google.com/g/2005#event.organizer' valueString='bachya1208#gmail.com'/>
<gd:when endTime='2011-09-29T11:30:00.000-06:00' startTime='2011-09-29T10:30:00.000-06:00'/>
<gd:transparency value='http://schemas.google.com/g/2005#event.opaque'/>
<gCal:anyoneCanAddSelf value='false'/>
<gCal:guestsCanInviteOthers value='true'/>
<gCal:guestsCanModify value='false'/>
<gCal:guestsCanSeeGuests value='true'/>
<gCal:sequence value='0'/>
<gCal:uid value='lp0upnpndnkp0ruqht7ef84kds#google.com'/>
</entry>
</feed>
XSLT:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template name="formatDateTime">
<xsl:param name="dateTime" />
<xsl:value-of select="concat(substring-before($dateTime, 'T'), ' ', substring-before(substring-after($dateTime, 'T'), '.'))" />
</xsl:template>
<xsl:template match="/">
<Events>
<xsl:apply-templates select="/*/*[local-name()= 'entry']" />
</Events>
</xsl:template>
<xsl:template match="*[local-name()= 'entry']">
<xsl:variable name="startDateTime" select="*[name() = 'gd:when']/#*[local-name() = 'startTime']" />
<xsl:variable name="endDateTime" select="*[name() = 'gd:when']/#*[local-name() = 'endTime']" />
<Event>
<EventTitle>
<xsl:value-of select="*[local-name() = 'title'][1]" />
</EventTitle>
<StartDateTime>
<xsl:call-template name="formatDateTime">
<xsl:with-param name="dateTime" select="$startDateTime" />
</xsl:call-template>
</StartDateTime>
<EndDateTime>
<xsl:call-template name="formatDateTime">
<xsl:with-param name="dateTime" select="$endDateTime" />
</xsl:call-template>
</EndDateTime>
<Who>
<xsl:value-of select="*[local-name() = 'author']/*[local-name() = 'name']" />
</Who>
<Where>
<xsl:value-of select="*[name() = 'gd:where']/#*[local-name() = 'valueString']" />
</Where>
<Status>
<xsl:value-of select="*[name() = 'gd:eventStatus']/#*[local-name() = 'value']" />
</Status>
</Event>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-16"?>
<Events>
<Event>
<EventTitle>Oil Change</EventTitle>
<StartDateTime>2011-09-29 10:30:00</StartDateTime>
<EndDateTime>2011-09-29 11:30:00</EndDateTime>
<Who>John Doe</Who>
<Where>9955 E Arapahoe Road, Englewood, CO 80112 (Go Subaru Arapahoe)</Where>
<Status>http://schemas.google.com/g/2005#event.confirmed</Status>
</Event>
</Events>
Your approach looks fine to me. I think your XPath code would be much cleaner and would probably run faster if you used regular element selection instead of local-name. The reason you probably struggled with your XPath was because you're consuming XML that has a default namespace of http://www.w3.org/2005/Atom, and that namespace isn't declared in your stylesheet. Here's a snippet of how a more simplified stylesheet could look, using an f: prefix for the feed namespace:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://www.w3.org/2005/Atom"
xmlns:gd="http://schemas.google.com/g/2005">
<!-- ... -->
<xsl:template match="/">
<Events>
<xsl:apply-templates select="//f:entry" />
</Events>
</xsl:template>
<xsl:template match="f:entry">
<xsl:variable name="startDateTime" select="gd:when/#startTime" />
<xsl:variable name="endDateTime" select="gd:when/#endTime" />
<Event>
<EventTitle>
<xsl:value-of select="f:title[1]" />
</EventTitle>
<StartDateTime>
<xsl:call-template name="formatDateTime">
<xsl:with-param name="dateTime" select="$startDateTime" />
</xsl:call-template>
</StartDateTime>
<EndDateTime>
<xsl:call-template name="formatDateTime">
<xsl:with-param name="dateTime" select="$endDateTime" />
</xsl:call-template>
</EndDateTime>
<Who>
<xsl:value-of select="f:author/f:name" />
</Who>
<Where>
<xsl:value-of select="gd:where/#valueString" />
</Where>
<Status>
<xsl:value-of select="gd:eventStatus/#value" />
</Status>
</Event>
</xsl:template>
<!-- etc -->
</xsl:stylesheet>
I would be inclined to replace the formatDateTime template with a match template:
<xsl:template match="#*" mode="formatDateTime">
<xsl:value-of select="concat(substring-before(., 'T'),
' ', substring-before(substring-after(., 'T'), '.'))" />
</xsl:template>
and change the calls to
<StartDateTime>
<xsl:apply-templates select="$startDateTime" mode="formatDateTime"/>
</StartDateTime>
<EndDateTime>
<xsl:apply-templates select="$endDateTime" mode="formatDateTime"/>
</EndDateTime>
Just because the call-template syntax is so verbose.
(and I would probably inline the variables too - they don't see to add value).
Here is a complete transformation that is derived from the provided, solving the default namespace problem (as already done by #Jacob), but also completely eliminating the unnecessary template matching the document node (/) and assuring that two unwanted namespaces will not appear on every (literal result) element in the output:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.w3.org/2005/Atom"
xmlns:gd="http://schemas.google.com/g/2005"
exclude-result-prefixes="a gd">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="a:entry[1]">
<Events>
<xsl:apply-templates select="../a:entry" mode="process"/>
</Events>
</xsl:template>
<xsl:template match="a:entry" mode="process">
<xsl:variable name="startDateTime" select="gd:when/#startTime" />
<xsl:variable name="endDateTime" select="gd:when/#endTime" />
<Event>
<EventTitle>
<xsl:value-of select="a:title[1]" />
</EventTitle>
<StartDateTime>
<xsl:call-template name="formatDateTime">
<xsl:with-param name="dateTime" select="$startDateTime" />
</xsl:call-template>
</StartDateTime>
<EndDateTime>
<xsl:call-template name="formatDateTime">
<xsl:with-param name="dateTime" select="$endDateTime" />
</xsl:call-template>
</EndDateTime>
<Who>
<xsl:value-of select="a:author/a:name" />
</Who>
<Where>
<xsl:value-of select="gd:where/#valueString" />
</Where>
<Status>
<xsl:value-of select="gd:eventStatus/#value" />
</Status>
</Event>
</xsl:template>
<xsl:template name="formatDateTime">
<xsl:param name="dateTime" />
<xsl:value-of select="concat(substring-before($dateTime, 'T'), ' ', substring-before(substring-after($dateTime, 'T'), '.'))" />
</xsl:template>
<xsl:template match="text()|a:entry"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gCal='http://schemas.google.com/gCal/2005' xmlns:gd='http://schemas.google.com/g/2005'>
<id>http://www.google.com/calendar/feeds/bachya1208%40gmail.com/public/full</id>
<updated>2011-09-19T21:32:50.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'/>
<title type='text'>John Doe</title>
<subtitle type='text'>John Doe</subtitle>
<link rel='alternate' type='text/html' href='https://www.google.com/calendar/embed?src=bachya1208#gmail.com'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full'/>
<link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/batch'/>
<link rel='self' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full?max-results=25'/>
<link rel='next' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full?start-index=26&max-results=25'/>
<author>
<name>John Doe</name>
<email>johndoe#gmail.com</email>
</author>
<generator version='1.0' uri='http://www.google.com/calendar'>Google Calendar</generator>
<openSearch:totalResults>1334</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<gCal:timezone value='America/Denver'/>
<gCal:timesCleaned value='0'/>
<entry>
<id>http://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/lp0upnpndnkp0ruqht7ef84kds</id>
<published>2011-09-14T21:15:16.000Z</published>
<updated>2011-09-14T21:15:16.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'/>
<title type='text'>Oil Change</title>
<content type='text'/>
<link rel='alternate' type='text/html' href='https://www.google.com/calendar/event?eid=bHAwdXBucG5kbmtwMHJ1cWh0N2VmODRrZHMgYmFjaHlhMTIwOEBt' title='alternate'/>
<link rel='self' type='application/atom+xml' href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/lp0upnpndnkp0ruqht7ef84kds'/>
<author>
<name>John Doe</name>
<email>johndoe#gmail.com</email>
</author>
<gd:comments>
<gd:feedLink href='https://www.google.com/calendar/feeds/johndoe%40gmail.com/public/full/lp0upnpndnkp0ruqht7ef84kds/comments'/>
</gd:comments>
<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'/>
<gd:where valueString='9955 E Arapahoe Road, Englewood, CO 80112 (Go Subaru Arapahoe)'/>
<gd:who email='johndoe#gmail.com' rel='http://schemas.google.com/g/2005#event.organizer' valueString='bachya1208#gmail.com'/>
<gd:when endTime='2011-09-29T11:30:00.000-06:00' startTime='2011-09-29T10:30:00.000-06:00'/>
<gd:transparency value='http://schemas.google.com/g/2005#event.opaque'/>
<gCal:anyoneCanAddSelf value='false'/>
<gCal:guestsCanInviteOthers value='true'/>
<gCal:guestsCanModify value='false'/>
<gCal:guestsCanSeeGuests value='true'/>
<gCal:sequence value='0'/>
<gCal:uid value='lp0upnpndnkp0ruqht7ef84kds#google.com'/>
</entry>
</feed>
the wanted, correct result is produced:
<Events>
<Event>
<EventTitle>Oil Change</EventTitle>
<StartDateTime>2011-09-29 10:30:00</StartDateTime>
<EndDateTime>2011-09-29 11:30:00</EndDateTime>
<Who>John Doe</Who>
<Where>9955 E Arapahoe Road, Englewood, CO 80112 (Go Subaru Arapahoe)</Where>
<Status>http://schemas.google.com/g/2005#event.confirmed</Status>
</Event>
</Events>