3

I'm trying to extract some information from an xml file using xslt. I've used the xslt math functions to output the differences between start and end times of some actions that I'm iterating over. However, I'm not certain how I can extend this to work out the difference between the start time of the next action and end time of the previous one. As far as I was aware you can't reassign a variable in xslt. In a conventional language I'd just store the last time encountered as variable. If anyone that could give me some pointers on how to do this in a more idiomatic xslt way I'd be most grateful.

Here's a simplified version of my data file.

<Actions>
    <Action>
            <Start>1</Start>
            <End>10</End>
    </Action>
    <Action>
            <Start>13</Start>
            <End>16</End>
    </Action>
    <Action>
            <Start>20</Start>
            <End>24</End>
    </Action>
</Actions>

This is my current xslt transformation that just does difference internally on actions.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
    <xsl:for-each select="Actions/Action">
        <xsl:value-of select="End - Start" /><xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

The result I'd like to see is

9
3
3
4
4
0

2 Answers 2

1

This short and simple transformation (no xsl:if, no axes):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:variable name="vValues" select="/*/*/*"/>

 <xsl:template match="/">
     <xsl:for-each select="$vValues[position() >1]">
       <xsl:variable name="vPos" select="position()"/>

       <xsl:value-of select=". - $vValues[$vPos]"/>
       <xsl:text>&#xA;</xsl:text>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<Actions>
    <Action>
        <Start>1</Start>
        <End>10</End>
    </Action>
    <Action>
        <Start>13</Start>
        <End>16</End>
    </Action>
    <Action>
        <Start>20</Start>
        <End>24</End>
    </Action>
</Actions>

produces the wanted, correct result:

9
3
3
4
4
Sign up to request clarification or add additional context in comments.

2 Comments

What about your favorite topic, I mean performance consideration?
@KirillPolishchuk: If you imply this solution isn't efficient, please, provide your arguments. I believe that this solution is quite efficient and can be performed even in streaming (or lazy evaluation) mode. In cases of an XSLT processor that performs no optimization and takes O(N) in order to evaluate $vValues[k], we can transform $vValues to another document where all values are contained in siblings elements, then for each element with position() > 1 output . - preceding-sibling::*[1]. This processing takes O(N). This generic solution applies to many different document structures.
1

Use:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/Actions/Action">
    <xsl:value-of select="End - Start"/>
    <xsl:if test="following-sibling::Action">
      <xsl:text>&#xA;</xsl:text>
      <xsl:value-of select="following-sibling::Action/Start - End"/>
      <xsl:text>&#xA;</xsl:text>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Output:

9
3
3
4
4

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.