使用 Saxon,递增或递减 XSLT 中的全局变量
Using Saxon, increment or decrement the global variables in XSLT
我想使用计数器来递增和递减 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
据我所知,在 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[. < 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[. < 14]"
.
至于你的最新要求,我认为你仍然可以使用 position()
来处理,只要你 select 你想要处理的元素,例如样式表
<?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[. < 10]"/>
</xsl:template>
<xsl:template match="record">
<xsl:value-of select="concat(position(), ' ', ., ' ')"/>
</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
没有 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>
(针对问题的展开描述进行回复)
要理解的关键是这个问题需要按文档顺序对 record
元素进行顺序处理。这意味着它不能以独立于处理器的方式使用 xsl:for-each 或 xsl:apply-templates 来实现,因为这些不能保证按顺序一次处理一个元素(最新版本的 Saxon ,例如,将使用多线程并行处理元素,并且规范允许这样做;这使得更新全局变量的任何尝试都无效)。
顺序处理元素的经典方法是使用头尾递归。编写一个模板或函数,将 record
元素的序列作为参数,并将当前行号作为第二个参数。如果记录列表非空,则 (a) 处理列表中的第一个 record
,并且 (b) 递归调用自己来处理列表的其余部分,传递行号的新值.
第一次尝试以这种方式编写代码时可能有点令人兴奋,但很快就会自然而然。
在 XSLT 3.0 中有一个新的构造 xsl:iterate,它看起来更像是一个传统的循环:当您进入 xsl:iterate 主体时,您为其参数传递初始值,并且每次您继续循环的下一次迭代,您可以为参数设置新值。
我想使用计数器来递增和递减 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
据我所知,在 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[. < 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[. < 14]"
.
至于你的最新要求,我认为你仍然可以使用 position()
来处理,只要你 select 你想要处理的元素,例如样式表
<?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[. < 10]"/>
</xsl:template>
<xsl:template match="record">
<xsl:value-of select="concat(position(), ' ', ., ' ')"/>
</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
没有 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>
(针对问题的展开描述进行回复)
要理解的关键是这个问题需要按文档顺序对 record
元素进行顺序处理。这意味着它不能以独立于处理器的方式使用 xsl:for-each 或 xsl:apply-templates 来实现,因为这些不能保证按顺序一次处理一个元素(最新版本的 Saxon ,例如,将使用多线程并行处理元素,并且规范允许这样做;这使得更新全局变量的任何尝试都无效)。
顺序处理元素的经典方法是使用头尾递归。编写一个模板或函数,将 record
元素的序列作为参数,并将当前行号作为第二个参数。如果记录列表非空,则 (a) 处理列表中的第一个 record
,并且 (b) 递归调用自己来处理列表的其余部分,传递行号的新值.
第一次尝试以这种方式编写代码时可能有点令人兴奋,但很快就会自然而然。
在 XSLT 3.0 中有一个新的构造 xsl:iterate,它看起来更像是一个传统的循环:当您进入 xsl:iterate 主体时,您为其参数传递初始值,并且每次您继续循环的下一次迭代,您可以为参数设置新值。