XPATH: Ignore milli seconds from date - xpath

I am trying to extract using the following
<xsl:template match="//alarms:alarmRaisedTime">
<xsl:variable name="secondsSince1970" select="(xs:dateTime(.) - xs:dateTime('1970-01-01T00:00:00')) div xs:dayTimeDuration('PT1S')" />
<xsl:element name="alarmRaisedTime" namespace="MY NAME SPACE">
<xsl:value-of select="$secondsSince1970"/>
</xsl:element>
</xsl:template>
Output I get is
<alarmRaisedTime>1367855105.001</alarmRaisedTime>
I would like to get the below given output (Note Milli Seconds is removed)
<alarmRaisedTime>1367855105</alarmRaisedTime>
I tried the following:
<xsl:value-of select="fn:substring-before($secondsSince1970,.)"/>
but it did not work.

Even simpler:
floor($secondsSince1970)
By definition:
The floor function returns the largest (closest to positive infinity) number that is not greater than the argument and that is an integer.

Try
<xsl:value-of select="fn:substring-before($secondsSince1970,'.')"/>
The . in your expression refers to the context item alarms:alarmsRaisedTime

Related

Unable to use apply-templates with node-set - XSLT

I am trying to get distinct values of shelf and add them to xml tree using node-set. I am trying to print the values of node-set using apply-templates. But I am doing something wrong. Kindly correct me. Thanks
<xsl:template name="form_list">
<xsl:for-each select="//library/section/#shelf[generate-id()= generate-id(key('distinct_shelfs',.)[1])]">
<xsl:variable name="shelfs">
<ml>
<m><xsl:value-of select="."/></m>
</ml>
</xsl:variable>
</xsl:for-each>
<xsl:variable name="Shelf_Set"><xsl:value-of select="exsl:node-set($shelfs)/ml"/></xsl:variable>
</xsl:template>
<xsl:call-template name="PrintShelfs"/>
<xsl:template name="PrintShelfs">
<xsl:apply-templates select="$Shelf_Set" mode="m1"/>
</xsl:template>
<xsl:template match="ml" mode="m1" >
<a><xsl:value-of select='.'/></a>
</xsl:template>
The xsl:value-of instruction flattens a node-set to a single text node. I suspect you want simply
<xsl:variable name="Shelf_Set" select="exsl:node-set($shelfs)/ml"/>

Breaking for-each loop on a conditional base

I'm new to xslt 2.0, I would like to set the value to a variable in for-each loop only once (means if the value set, I want to come out of the loop).
For now it keep iterating for all the users. I just want to come out of the loop once the value set (immediately after my first attemp). I'm not sure how to break if the value set once.
Can you please help me on the below code ?
XSLT Code:
<xsl:variable name="v_first_name">
<xsl:for-each select="$emailList/emails/child::*">
<xsl:variable name="mailid" select="id" />
<xsl:for-each select="$userList/users/child::*">
<xsl:if test="emailid = $mailid">
<xsl:if test="firstname eq 'Antony'">
<xsl:value-of select="firstname" />
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:if test="$v_first_name != ''">
<first_name>
<xsl:value-of select="$v_first_name" />
</first_name>
</xsl:if>
XML O/p:
<first_name>AntonyAntonyAntonyAntony</first_name>
Expected XML O/P:
<first_name>Antony</first_name>
Note1: Please note that I'm using xslt 2.0 and my lists can have duplicates (So Antony can come twice, but I want only once (or unique)).
Note2: I also tried with position(), but couldn't find it work as the condition () can match at any position.
Thanks in advance.
Start with XPath and simply select the nodes you are looking for instead of considering for-each a "loop". If you select e.g. $userList/users/*[emailid = $emailList/emails/*/id] you select child elements from users which have a matching emailid in $emailList/emails/*.
I am not sure which sense it makes to hard code a first name value and then output it but of course you can select e.g. $userList/users/*[emailid = $emailList/emails/*/id and firstname = 'Antony']/lastname. That gives you a sequence of element nodes, if you want the first use a positional predicate e.g. depending on the structure of your input $userList/users/*[emailid = $emailList/emails/*/id and firstname = 'Antony'][1]/lastname or, of all selected elements ($userList/users/*[emailid = $emailList/emails/*/id and firstname = 'Antony']/lastname)[1].

using preceding-sibling with with xsl:sort

I'm trying to use preceding-sibling and following-sibling with a subset of records with a sort on them. The problem that the preceding / following brings back values from the original xml order:
<Salaries>
<Salary>
<Base>1000</Base>
<CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System">
<d7p1:DateTime>2016-01-09T14:38:54.8440764Z</d7p1:DateTime>
<d7p1:OffsetMinutes>0</d7p1:OffsetMinutes>
</CreatedDate>
</Salary>
<Salary>
<Base>2000</Base>
<CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System">
<d7p1:DateTime>2015-01-09T14:38:54.8440764Z</d7p1:DateTime>
<d7p1:OffsetMinutes>0</d7p1:OffsetMinutes>
</CreatedDate>
</Salary>
<Salary>
<Base>3000</Base>
<CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System">
<d7p1:DateTime>2017-01-09T14:38:54.8440764Z</d7p1:DateTime>
<d7p1:OffsetMinutes>0</d7p1:OffsetMinutes>
</CreatedDate>
</Salary>
</Salaries>
When I use a sort under a for-each (Salaries/Salary) with a c# function to add offset minutes into a date and convert to a long number 201701010000 for example(to make manipulation in xslt easier).
<xsl:sort select="number(cs:Convertdatetolong(cs:AddOffsetMinutes(substring(p:CreatedDate/d5p1:DateTime,1,19),p:CreatedDate/d5p1:OffsetMinutes)))" order="ascending"/>
The sort works perfectly and I get the records out in the following order:
2000
1000
3000
The problem comes if I use preceding-sibling / preceding (and following). I would expect the first record (2000) to have no preceding record and the last record (3000) to have no following.
However when I use the preceding / following I get the previous record and the next record from the original XML:
2000 (preceding - 1000 / following - 3000)
1000 (preceding - / following - 2000)
3000 (preceding - 2000 / following - )
I would like to be able to compare against the previous record (in the sorted order) and the current record (in the sorted order):
2000 (preceding - / following - 1000)
1000 (preceding - 2000 / following 3000)
3000 (preceding - 1000 / following - )
I've tried preceding-sibling and preceding
<xsl:value-of select="preceding::p:Salary[1]/p:Base"/>
<xsl:value-of select="preceding-sibling::p:Salary[1]/p:Base"/>
<xsl:value-of select="preceding::p:Salary[position()=1]/p:Base"/>
(the salary is in a different namespace (p)
Is this actually possible or do I have to use variables to save the previous record's data to compare against?
Any ideas gratefully received. I'm using xslt 1.0
Although XSLT/XPath often talks of a "sequence of nodes", it's actually more accurate to think of it as a "sequence of node references" - because, for example, the same node can appear more than once in the sequence. When you sort a sequence of node references, you don't change the individual nodes in any way, you only change the sequence. That means the nodes still exist in their original tree exactly where they were before, and their parents, siblings, and descendants are exactly as they were before.
What you want is not the preceding and following siblings of the node, but the nodes that come before and after it in the sorted sequence, which is a quite different thing.
One way to do this is to construct a new tree containing copies of the original nodes, which you get, for example, if you do
<xsl:variable name="x">
<xsl:for-each ...>
<xsl:sort ...>
<xsl:copy-of select="."/>
The sibling relationships of the copied nodes will then reflect the sorted order. There's the minor problem that in XSLT 1.0, $x is a result tree fragment so you have to convert it to a node-set using the exslt:node-set() function.
In fact in XSLT 1.0 that's probably the only way of doing it, because the XSLT 1.0 data model only has node sets, not sequences, which means there is no way of capturing and processing a sequence of nodes in anything other than document order. The 2.0 model has much more flexibility and power. Upgrade if you can - XSLT 1.0 is approaching 20 years old.
Thanks to Michael for the answer. Posted here for completeness. Complicated because of the name spaces in use in the xml:
<!-- Puts the whole of the Salary Node into a variable-->
<xsl:variable name="SALARY" >
<xsl:copy-of select="p:Salaries" />
</xsl:variable>
<!-- Puts the the required key data into a node-set with the correct sort applied-->
<xsl:variable name="SAL">
<xsl:for-each select="msxsl:node-set($SALARY)//p:Salary">
<xsl:sort select="number(cs:Convertdatetolong(cs:AddOffsetMinutes(substring(p:CreatedDate/d5p1:DateTime,1,19),p:CreatedDate/d5p1:OffsetMinutes)))" order="ascending"/>
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
<!-- Quick Output-->
<xsl:for-each select="msxsl:node-set($SAL)//p:Salary">
<xsl:text>Sa:</xsl:text>
<xsl:value-of select="position()" />
<xsl:text>Preceding:</xsl:text>
<xsl:value-of select="preceding-sibling::p:Salary[1]/p:Base"/>
<xsl:value-of select="$newline" />
<xsl:text>Current:</xsl:text>
<xsl:value-of select="p:Base"/>
<xsl:value-of select="$newline" />
<xsl:text>Following:</xsl:text>
<xsl:value-of select="following-sibling::p:Salary[1]/p:Base"/>
<xsl:value-of select="$newline"/>
</xsl:for-each>
The preceding-sibling axis gets the preceding siblings of the context node in document order.
To refer to the preceding siblings of a node after sorting, you will need to store the sorted nodes in a variable first - and, in XSLT 1.0, convert the variable into a node-set.

Treating empty document fragments as empty results

What is the most clean way to treat a variable that contains an empty document fragment as an empty variable?
I have the following code
<xsl:variable name="embedded-examples" select="d:example"/>
<xsl:variable name="external-examples">
<xsl:call-template name="external-examples"/>
</xsl:variable>
<xsl:variable name="examples" select="$embedded-examples | $external-examples"/>
<xsl:if test="$examples">
<d:section>
<d:title>
<xsl:text>Examples</xsl:text>
</d:title>
<xsl:for-each select="$examples">
<xsl:copy-of select="."/>
</xsl:for-each>
</d:section>
</xsl:if>
And the template
<xsl:template name="external-examples">
<xsl:variable name="example-comments" select="$example-docs//comment()"/>
<xsl:for-each select="$example-comments">
<d:example>
<d:title><xsl:value-of select="normalize-space(.)"/></d:title>
</d:example>
</xsl:for-each>
</xsl:template>
The problem is that when the variable in the esternal-examples is empty the xsl:for-each loop is not run, but it produces an empty document fragment anyhow, making the test test="$examples" pass instead of fail.
What should I do in the template to make sure that when example-comments is empty the template returns nothing?
Use a predicate to check for child nodes:
<xsl:for-each select="$example-comments[count(child::node() ) > 0]">
...
</xsl:for-each>

for-each no results text

I have a for-each and when there is nothing output by it I would like to display some default text.
If I have...
<xsl:for-each select='xpath'> Some content for each matching element result. </xsl:for-each>
What I would like is:
<xsl:for-each select='xpath'>
<xsl:output-this-when-there-is-content> Some content for each matching element result. </xsl:output-this-when-there-is-content>
<xsl:no-results-output> Some content for when the for-each finds no matches. </xsl:no-results-output>
</xsl:for-each>
Can anyone tell me how to do this, please?
Thanks,
Matt
Assuming you have:
<xsl:for-each select="xpath"> ...
The you can do something like:
<xsl:choose>
<xsl:when test="xpath">
<xsl:for-each select="xpath"> ...
</xsl:when>
<xsl:otherwise>
<xsl:text>Some default text</xsl:text>
</xsl:otherwise>
</xsl:choose>
To avoid the double test of the XPath (and duplication) you could probably use an xsl:variable, something like the following (syntax may be a little wrong, but the rough idea should be right).
<xsl:choose>
<xsl:variable name="elems" select="xpath"/>
<xsl:when test="$elems">
<xsl:for-each select="$elems"> ...
</xsl:when>
<xsl:otherwise>
<xsl:text>Some default text</xsl:text>
</xsl:otherwise>
</xsl:choose>
To avoid the verbosity of the <xsl:choose> solution that Greg Beech proposed, you can do:
<xsl:variable name="elems" select="xpath"/>
<xsl:for-each select="$elems">
<!-- ... -->
</xsl:for-each>
<xsl:if test="not($elems)">
<xsl:text>Some default text</xsl:text>
</xsl:if>
The <xsl:variable> is for efficiency, it avoids doing the same query twice.
The <xsl:for-each> only runs if there are any nodes in $elems, the <xsl:if> only runs if there are not.
I would think sth like this would be good:
If (x!=null)
{
console.write("Default Text")}
else
{
foreach (var y in x)
{
Console.Writeline(y);
//...
}
}

Resources