I have XML form data coming in from a vendor app that allows me to select values for fields on the form by using this format.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text" /><xsl:template match="/"><xsl:apply-templates/></xsl:template>
<xsl:template match="DataSet/diffgr:diffgram/Forms">
<xsl:for-each select="Form">
<xsl:value-of select="FieldName1"/>
<xsl:value-of select="FieldName2"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I can't see the XML, but I can surmise from this that all of the fields are siblings within the <form> and looks something like this:
<form>
<CosignerName1 value="John Doe"/>
<CosignerDate1 value="1/1/1970"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Jane Smith"/>
<CosignerDate2 value="2/2/1972"/>
<CosignerTime2 value="11:46 AM"/>
...
<CosignerName12 value="Will Hunting"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 AM"/>
</form>
<form>
<CosignerName1 value="Bill Thomas"/>
<CosignerDate1 value="5/5/2020"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Bev Poole"/>
<CosignerDate2 value="6/6/2022"/>
<CosignerTime2 value="11:46 AM"/>
...
<CosignerName12 value="Bob Ross"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 AM"/>
</form>
Known factors:
Each form has 12 cosigners, each with numbered field names i.e. CosignerName1, CosignerName2, etc.
Each named cosigner also has a CosignerDate and a CosignerTime field associated by number. i.e. CosignerName1 has CosignerDate1 and CosignerTime1 fields that, when combined, show when that user signed the form.
I am using the word 'idnum' in my code and in this question to refer to the number at the end of each label that shows they are part of a set i.e CosignerName1, CosignerDate1, and CosignerTime1 all share idnum=1
Goal: Identify and select only the most recent CosignerName, based on CosignerDate and CosignerTime.
To do this, I figure I need to:
Find the most recent CosignerDate/CosignerTime combination
Read the label of either of the identified fields to extract the idnum from the end
Use the idnum to to identify the correct CosignerName and output it
I have some pieces of the solution, but no way to bring them together:
Create a Timestamp
I can combine a set of CosignerDate[idnum] and CosignerTime[idnum] fields, then translate them into a timestamp. If I could create an array of these timestamps, then I could sort and find the most recent one, but as I understand it XSLT 1.0 doesn't have arrays. So I am not sure what to do with this.
<xsl:variable name="mm" select="substring-before(CosignerDate1,'/')" />
<xsl:variable name="mmyyyy" select="substring-after(CosignerDate1,'/')"/>
<xsl:variable name="dd" select="substring-before($mmyyyy,'/')" />
<xsl:variable name="yyyy" select="substring-after($mmyyyy,'/')" />
<xsl:variable name="ampm" select="substring-after(CosignerTime1,' ')" />
<xsl:variable name="time">
<xsl:choose>
<xsl:when test="$ampm = 'AM'">
<xsl:value-of select="number(translate(substring-before(CosignerTime1,' '),':',''))" />
</xsl:when>
<xsl:when test="$ampm = 'PM'">
<xsl:value-of select="number(translate(substring-before(CosignerTime1,' '),':',''))+number('1200')" />
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat('Timestamp:',$yyyy,$mm,$dd,$time)"/>
The code above outputs something like: Timestamp:202209191128
Get idnums
I can loop through all of the Cosigners and extract the identifying number shared by their labels.
<xsl:for-each select="*[starts-with(local-name(), 'CosignerName')]">
<xsl:variable name="idnum">
<xsl:value-of select="translate(local-name(),'CosignerName','')" />
</xsl:variable>
<xsl:value-of select="$idnum"/>
<xsl:value-of select="','"/>
</xsl:for-each>
This code above outputs something like: 1,4,7,12
But, this is where I get lost.
Failed Attempt
I tried something like the code below. The logic was to loop through each Cosigner name (12 iterations), get the idnum from the label, use that to get the CosignerDate and CosignerTime, create a timestamp, then sort the timestamps, if the timestamp we are looking at is the top one (most recent) then use the idnum to select the correct CosignerName and output it.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text" /><xsl:template match="/"><xsl:apply-templates/></xsl:template>
<xsl:template match="DataSet/diffgr:diffgram/Forms">
<xsl:for-each select="Form">
<xsl:for-each select="*[starts-with(local-name(), 'CosignerName')]">
<xsl:variable name="idnum">
<xsl:value-of select="translate(local-name(),'CosignerName','')" />
</xsl:variable>
<xsl:variable name="mm" select="substring-before(concat(CosignerDate,$idnum),'/')" />
<xsl:variable name="mmyyyy" select="substring-after(concat(CosignerDate,$idnum),'/')"/>
<xsl:variable name="dd" select="substring-before($mmyyyy,'/')" />
<xsl:variable name="yyyy" select="substring-after($mmyyyy,'/')" />
<xsl:variable name="ampm" select="substring-after(concat(CosignerTime,$idnum),' ')" />
<xsl:variable name="time">
<xsl:choose>
<xsl:when test="$ampm = 'AM'">
<xsl:value-of select="number(translate(substring-before(concat(CosignerTime,$idnum),' '),':',''))" />
</xsl:when>
<xsl:when test="$ampm = 'PM'">
<xsl:value-of select="number(translate(substring-before(concat(CosignerTime,$idnum),' '),':',''))+number('1200')" />
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="timestamp" select="concat('Timestamp: ',$yyyy,$mm,$dd,$time)"/>
<xsl:sort select="$timestamp" data-type="number" order="descending"/>
<xsl:if test="position() = 1"><xsl:value-of select="."/></xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This of course fails for several reasons:
the concat with the $idnum doesn't actually pull values from fields
the sort has to be the first thing in the for-each
the sort has to happen after all of the timestamps are created
the sorted list of timestamps still needs to reference which idnum they were generated from
Without being able to create an array of timestamps and idnums, I don't know how you can accomplish this.
Does anyone know how can I get at the CosignerName[idnum] that is correlated with the most recent CosignerDate[idnum]/CosignerTime[idnum]?
I believe you could just sort the CosignerNameX nodes by the individual components of their associated dates and times, as shown in the two other answers I linked to:
https://stackoverflow.com/a/59288030/3016153
https://stackoverflow.com/a/30631073/3016153
However, it might be more efficient - and certainly more readable - to do this in two steps:
Construct a dateTime value for each cosigner;
Find the cosigner with the most recent value.
Consider the following example:
XML
<DataSet xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<diffgr:diffgram>
<Forms>
<form>
<CosignerName1 value="John Doe"/>
<CosignerDate1 value="1/1/1970"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Jane Smith"/>
<CosignerDate2 value="2/2/1972"/>
<CosignerTime2 value="11:46 AM"/>
<CosignerName12 value="Will Hunting"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 AM"/>
</form>
<form>
<CosignerName1 value="Bill Thomas"/>
<CosignerDate1 value="5/5/2020"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Bev Poole"/>
<CosignerDate2 value="6/6/2022"/>
<CosignerTime2 value="11:46 AM"/>
<CosignerName12 value="Bob Ross"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 PM"/>
</form>
</Forms>
</diffgr:diffgram>
</DataSet>
XSLT 1.0 + EXSLT node-set()
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="diffgr exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/DataSet">
<output>
<xsl:for-each select="diffgr:diffgram/Forms/form">
<xsl:variable name="datetimes">
<xsl:for-each select="*[starts-with(local-name(), 'CosignerName')]">
<!-- identify values -->
<xsl:variable name="i" select="substring-after(local-name(), 'CosignerName')" />
<xsl:variable name="date" select="../*[local-name()=concat('CosignerDate', $i)]/#value" />
<xsl:variable name="time" select="../*[local-name()=concat('CosignerTime', $i)]/#value" />
<!-- extract date components -->
<xsl:variable name="year" select="substring-after(substring-after($date, '/'), '/')" />
<xsl:variable name="month" select="substring-before($date, '/')" />
<xsl:variable name="day" select="substring-before(substring-after($date, '/'), '/')" />
<!-- extract time components -->
<xsl:variable name="hour12" select="substring-before($time, ':')" />
<xsl:variable name="minute" select="substring-before(substring-after($time, ':'), ' ')" />
<xsl:variable name="pm" select="contains($time,'PM')" />
<xsl:variable name="hour" select="$hour12 mod 12 + 12*$pm"/>
<!-- construct dateTime -->
<datetime cosigner="{#value}" index="{$i}">
<xsl:value-of select="$year"/>
<xsl:value-of select="format-number($month, '-00')"/>
<xsl:value-of select="format-number($day, '-00')"/>
<xsl:value-of select="format-number($hour, 'T00')"/>
<xsl:value-of select="format-number($minute, ':00')"/>
<xsl:text>:00</xsl:text>
</datetime>
</xsl:for-each>
</xsl:variable>
<!-- output -->
<form>
<xsl:for-each select="exsl:node-set($datetimes)/datetime">
<xsl:sort select="." data-type="text" order="descending"/>
<xsl:if test="position()=1">
<last-cosigner index="{#index}" dateTime="{.}" >
<xsl:value-of select="#cosigner"/>
</last-cosigner>
</xsl:if>
</xsl:for-each>
</form>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<form>
<last-cosigner index="12" dateTime="1982-12-12T01:00:00">Will Hunting</last-cosigner>
</form>
<form>
<last-cosigner index="2" dateTime="2022-06-06T11:46:00">Bev Poole</last-cosigner>
</form>
</output>
P.S. With a Microsoft processor you may need to change the namespace declaration:
xmlns:exsl="http://exslt.org/common"
to:
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
and then change the exsl prefix in lines #5 and #39 to msxsl.
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 am learning XSLT and trying to transform below xml. But couldn't achieve the task
<sample id="7">
<land1 id="8">
<owner>TOMMY</owner>
<type>INDIVIDUAL</type>
<hint>TOM_INDIVIDUAL</hint>
<date>12.02.2014</date>
<text>land details</text>
<number>1</number>
<cost>WIDERRUFLICH</cost>
</land1>
</sample>
and trying to convert above into
<table name="sample">
<tablename="land1">
<rel name="owner" value="TOMMY"/>
<rel name="type" value="INDIVIDUAL"/>
<rel name="<hint" value="TOM_INDIVIDUAL"/>
<rel name="date" value="12.02.2014"/>
<rel name="details" value="land details"/>
<rel name="number" value="1"/>
<rel name="cost" value="25%"/>
</table>
</table>
I tried below to generate the same, but it's not working.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:if test="current().count(*)>0">
<xsl:element name="table">
<xsl:attribute name="name">
<xsl:value-of select="name(.)"/>
</xsl:attribute>
<xsl:apply-templates select="/"
</xsl:element>
</xsl:if>
<xsl:if test="current().count(*)=0">
<xsl:element name="rel">
<xsl:attribute name="name">
<xsl:value-of select="name(.)"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="current()"/>
</xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Can someone please let me know where I am doing wrong?
Can someone please let me know where I am doing wrong?
Well, for one thing, current().count(*)>0 is not a valid expression.
And you have <xsl:apply-templates select="/" without closing the tag. Which may be a good thing - because if it worked, it would have created an infinite loop.
I also don't understand the overall logic of your approach. Couldn't you do simply:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*[*]">
<table name="{name()}">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="*">
<rel name="{name()}" value="{.}"/>
</xsl:template>
</xsl:stylesheet>
Added:
every element with an attribute should also be a table. For example,
in the above xml, <land1 id="8"> has children and your logic works
fine. But my inout can also contain <land1 id="8"/>. Now even though
it doesn't have any child elements, still element name has to be
considered as Table
Then use:
<xsl:template match="*[#*]">
instead of:
<xsl:template match="*[*]">
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.
When I try to transform this XAML document
<Section xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve" TextAlignment="Left" LineHeight="Auto" IsHyphenationEnabled="False" xml:lang="en-us" FlowDirection="LeftToRight" NumberSubstitution.CultureSource="User" NumberSubstitution.Substitution="AsCulture" FontFamily="Arial" FontStyle="Normal" FontWeight="Normal" FontStretch="Normal" FontSize="12" Foreground="#FF000000" Typography.StandardLigatures="True" Typography.ContextualLigatures="True" Typography.DiscretionaryLigatures="False" Typography.HistoricalLigatures="False" Typography.AnnotationAlternates="0" Typography.ContextualAlternates="True" Typography.HistoricalForms="False" Typography.Kerning="True" Typography.CapitalSpacing="False" Typography.CaseSensitiveForms="False" Typography.StylisticSet1="False" Typography.StylisticSet2="False" Typography.StylisticSet3="False" Typography.StylisticSet4="False" Typography.StylisticSet5="False" Typography.StylisticSet6="False" Typography.StylisticSet7="False" Typography.StylisticSet8="False" Typography.StylisticSet9="False" Typography.StylisticSet10="False" Typography.StylisticSet11="False" Typography.StylisticSet12="False" Typography.StylisticSet13="False" Typography.StylisticSet14="False" Typography.StylisticSet15="False" Typography.StylisticSet16="False" Typography.StylisticSet17="False" Typography.StylisticSet18="False" Typography.StylisticSet19="False" Typography.StylisticSet20="False" Typography.Fraction="Normal" Typography.SlashedZero="False" Typography.MathematicalGreek="False" Typography.EastAsianExpertForms="False" Typography.Variants="Normal" Typography.Capitals="Normal" Typography.NumeralStyle="Normal" Typography.NumeralAlignment="Normal" Typography.EastAsianWidths="Normal" Typography.EastAsianLanguage="Normal" Typography.StandardSwashes="0" Typography.ContextualSwashes="0" Typography.StylisticAlternates="0">
<Table CellSpacing="1" Margin="0,0,0,0"><Table.Columns><TableColumn Width="264" /></Table.Columns><TableRowGroup><TableRow><TableCell Padding="0,0,0,0">
<Paragraph FontFamily="Times New Roman" FontSize="10.666666666666666" Margin="0,0,0,0">
<Span FontWeight="Bold"><Run>some text</Run></Span><Run> </Run>
<Span Foreground="#FF00681C"><Run>some more text</Run></Span>
</Paragraph>
</TableCell></TableRow></TableRowGroup></Table>
</Section>
using this XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="/Table"/>
<xsl:apply-templates select="/Paragraph"/>
<xsl:apply-templates select="/Run"/>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Table">
<table>
<xsl:apply-templates select="TableRowGroup"/>
</table>
</xsl:template>
<xsl:template match="TableRowGroup">
<xsl:apply-templates select="TableRow"/>
</xsl:template>
<xsl:template match="TableRow">
<tr>
<xsl:apply-templates select="TableCell"/>
</tr>
</xsl:template>
<xsl:template match="TableCell">
<td>
</td>
</xsl:template>
<xsl:template match="Paragraph">
<p>
<xsl:apply-templates select=""/>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="Run">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="Span">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
</xsl:stylesheet>
i get this incorrect result
<html>
<body>
some text some more text
</body>
I saw a few problems with your XSLT that would stop it working.
There is no namespace in the XSLT. There needs to be a namespace that
matches the one in the input XML, and you need to use this namespace
when matching elements.
Your first template matches "/", which matches the root node. I
assume you actually want to match the document element Section. The
apply-templates inside this are matching elements beginning with
"/", which makes them absolute paths, so they won't match anything.
I've taken a guess at what output XML you expect. The following XSLT stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
version="1.0"
exclude-result-prefixes="ns">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" method="html"/>
<xsl:template match="/ns:Section">
<html>
<body>
<xsl:apply-templates select="ns:Table"/>
</body>
</html>
</xsl:template>
<xsl:template match="ns:Table">
<table>
<xsl:apply-templates select="ns:TableRowGroup"/>
</table>
</xsl:template>
<xsl:template match="ns:TableRowGroup">
<xsl:apply-templates select="ns:TableRow"/>
</xsl:template>
<xsl:template match="ns:TableRow">
<tr>
<xsl:apply-templates select="ns:TableCell"/>
</tr>
</xsl:template>
<xsl:template match="ns:TableCell">
<td>
<xsl:apply-templates select="ns:Paragraph"/>
</td>
</xsl:template>
<xsl:template match="ns:Paragraph">
<p>
<xsl:apply-templates select="ns:Span"/>
</p>
</xsl:template>
<xsl:template match="ns:Run">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="ns:Span">
<span>
<xsl:apply-templates select="ns:Run"/>
</span>
</xsl:template>
</xsl:stylesheet>
produces the following output when applied to your example input XML:
<html>
<body>
<table>
<tr>
<td>
<p><span><span>some text</span></span><span><span>some more text</span></span></p>
</td>
</tr>
</table>
</body>
</html>
which may need a little formatting...