使用Saxon,在XSLT中递增或递减全局变量

时间:2016-05-24 10:32:28

标签: xslt saxon xalan

我想使用计数器来增加和减少XSLT中的值。我能够使用Apache Xalan实现这一点,但现在我想用Saxon实现同样的目标。

Xalan的XSLT脚本如下所示:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:lxslt="http://xml.apache.org/xslt"
                xmlns:result="http://www.example.com/results"
                extension-element-prefixes="result">

    <lxslt:component prefix="result" functions="incr dcr">
        <lxslt:script lang="javascript">
            var count = 0;
            function incr() {
              count++;
              return count;
            }

            function dcr() {
              count--;
              return count;
            }
        </lxslt:script>
    </lxslt:component>

    <xsl:template match="/">
        a)<xsl:value-of select="result:incr()"/>
        b)<xsl:value-of select="result:incr()"/>
        c)<xsl:value-of select="result:dcr()"/>
    </xsl:template>

</xsl:stylesheet>

预期输出为:

a)1
b)2
c)1

------------------------- my use case using saxon -------------------
This is my sample data.xml file. I want to transform this to html file.

<entity>
  <record>10</record>
  <record>15</record>
  <record>19</record>
  <record>7</record>
  <record>4</record>
  <record>14</record>
  <record>24</record>
<entity>

I want to implement a counter for line-number and want to increment the counter and print the line-number
my expected outputs:

 case 1: If record values > 14, then my output should have line-number with value as.
 line-num  value    
  1        15
  2        19
  3        24

 case 2 : If record values < 14, then my output should have line-number with value as.
 line-num  value    
  1         10
  2         7
  3         4

My other use case :

<entity>
  <record>10</record>
  <record>15</record>
  <record>19</record>
  <record>7</record>
  <record>20</record>
  <record>14</record>
  <record>24</record>
    <entity>
       <record>30</record>
       <record>3</record>
    </entity>
</entity>
<entity>
  <record>5</record>
  <record>17</record>
  <record>19</record>
  <record>6</record>
  <record>70</record>
  <record>9</record>
  <record>35</record>
    <entity>
       <record>15</record>
       <record>2</record>
    </entity>
</entity>


This is my other use case, first <entity> record value > 15 and in second <entity> record value < 10, and my <entity> can grow bigger where i have to show only some record based on condition.

 line-num  value 
    1        19 
    2        20  
    3        24
    4        30 
    5        5
    6        6
    7        2   

3 个答案:

答案 0 :(得分:1)

据我所知,在Saxon中无法使用Javascript。

如果您想在Saxon 9(EE或PE)中使用可分配和可更改的变量,那么您可以使用

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:mf="http://example.com/mf"
    extension-element-prefixes="saxon"
    exclude-result-prefixes="xs saxon mf"
    version="2.0">

    <xsl:variable name="counter" as="xs:integer" select="0" saxon:assignable="yes"/>

    <xsl:function name="mf:incr" as="xs:integer">
        <saxon:assign name="counter" select="$counter + 1"/>
        <xsl:sequence select="$counter"/>
    </xsl:function>

    <xsl:function name="mf:decr" as="xs:integer">
        <saxon:assign name="counter" select="$counter - 1"/>
        <xsl:sequence select="$counter"/>
    </xsl:function>

    <xsl:template match="/" name="main">
        a)<xsl:value-of select="mf:incr()"/>
        b)<xsl:value-of select="mf:incr()"/>
        c)<xsl:value-of select="mf:decr()"/>
    </xsl:template>

</xsl:stylesheet>

请参阅http://saxonica.com/html/documentation/extensions/instructions/assign.html中有关引入副作用的警告和注释。您可能想编辑您的问题以解释您的XML输入和所需的输出以及您认为需要这样的计数器变量的原因,希望我们可以显示纯XSLT 2.0解决方案而不是使用可赋值变量。

至于您处理和编号某些输入元素的要求,样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output indent="yes"/>

    <xsl:template match="entity">
        <root>
            <xsl:apply-templates select="record[. > 14]"/>
            <xsl:apply-templates select="record[. &lt; 14]"/>
        </root>
    </xsl:template>

    <xsl:template match="record">
        <xsl:copy>
            <line-num>
                <xsl:value-of select="position()"/>
            </line-num>
            <value>
                <xsl:value-of select="."/>
            </value>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

对输入

运行时
<entity>
    <record>10</record>
    <record>15</record>
    <record>19</record>
    <record>7</record>
    <record>4</record>
    <record>14</record>
    <record>24</record>
</entity>

输出

<root>
   <record>
      <line-num>1</line-num>
      <value>15</value>
   </record>
   <record>
      <line-num>2</line-num>
      <value>19</value>
   </record>
   <record>
      <line-num>3</line-num>
      <value>24</value>
   </record>
   <record>
      <line-num>1</line-num>
      <value>10</value>
   </record>
   <record>
      <line-num>2</line-num>
      <value>7</value>
   </record>
   <record>
      <line-num>3</line-num>
      <value>4</value>
   </record>
</root>

其他选项正在使用,例如xsl:number count="entity[. &lt; 14]"

至于您的最新要求,我认为只要您选择要处理的元素,您仍然可以使用position()处理该问题,例如:样式表

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

    <xsl:output method="text"/>

    <xsl:template match="/">
        <xsl:apply-templates select="/root/entity[1]//record[. > 15], /root/entity[2]//record[. &lt; 10]"/>
    </xsl:template>

    <xsl:template match="record">
        <xsl:value-of select="concat(position(), ' ', ., '&#10;')"/>
    </xsl:template>

</xsl:stylesheet>

应用于输入

<root>
    <entity>
        <record>10</record>
        <record>15</record>
        <record>19</record>
        <record>7</record>
        <record>20</record>
        <record>14</record>
        <record>24</record>
        <entity>
            <record>30</record>
            <record>3</record>
        </entity>
    </entity>
    <entity>
        <record>5</record>
        <record>17</record>
        <record>19</record>
        <record>6</record>
        <record>70</record>
        <record>9</record>
        <record>35</record>
        <entity>
            <record>15</record>
            <record>2</record>
        </entity>
    </entity>
</root>

输出(使用像Saxon 9这样的XSLT 2.0处理器)

1 19
2 20
3 24
4 30
5 5
6 6
7 9
8 2

答案 1 :(得分:1)

(回应问题的扩展描述)

要理解的关键是此问题需要按文档顺序对record元素进行顺序处理。这意味着它不能以与处理器无关的方式使用xsl:for-each或xsl:apply-templates实现,因为这些不能保证按顺序一次处理一个元素(Saxon的最新版本,for例如,将使用多线程并行处理元素,规范允许这样做;这使得更新全局变量的任何尝试都无效。)

按顺序处理元素的经典方法是使用头尾递归。编写一个模板或函数,将record元素的序列作为参数,将当前行号作为第二个参数。如果记录列表非空,则(a)处理列表中的第一个record,以及(b)递归调用自己处理列表的其余部分,传递行号的新值

第一次尝试编写代码时可能会有点令人兴奋,但很快就会自然而然。

在XSLT 3.0中,新的构造xsl:iterate看起来更像传统的循环:当你输入xsl:iterate体时,你传递其参数的初始值,每次你继续下一次迭代您可以为循环设置参数的新值。

答案 2 :(得分:0)

没有javascript,使用撒克逊扩展指令。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:saxon="http://saxon.sf.net/"
                extension-element-prefixes="saxon"
                exclude-result-prefixes="saxon">
    <xsl:variable name="i" select="0" saxon:assignable="yes"/>
    <xsl:template match="/">
        a) <xsl:value-of select="$i"/>
            <saxon:assign name="i" select="$i+1"/>
        b) <xsl:value-of select="$i"/>
            <saxon:assign name="i" select="$i+2"/>
        c) <xsl:value-of select="$i"/>
            <saxon:assign name="i" select="$i+3"/>
        d) <xsl:value-of select="$i"/>
    </xsl:template>
</xsl:stylesheet>