在 XSLT 中实现动态 xpath:for-each-group
Implement dynamic xpath in XSLT: for-each-group
我正在尝试使用 saxon:evaluate 来减少 xsl 函数中的重复代码。
但是我尝试的任何事情 returns 都是错误的。
这是一段重复代码。
<!--Select by outputclass and group by attribute-->
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[contains(@outputclass,$sel)]" group-by="@*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
<!--Select by node and group by child node-->
<xsl:when test="$from='node-childnode'">
<xsl:for-each-group select="$compose//*[name()=$sel]" group-by="child::*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
我想要的是传递一个参数,指示选择的是元素名称还是输出类属性。
然后是另一个参数,指示分组依据:属性或父节点、子节点、节点之后或之前的节点。
我试过的如下:
<xsl:variable name="oSel">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:value-of select="$compose//*[contains(@outputclass,$sel)]"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:value-of select="$compose//*[name()=$sel]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="oGroup">
<xsl:choose>
<xsl:when test="ends-with($from,'-attribute')">
<xsl:value-of select="*/@*[local-name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-childnode')">
<xsl:value-of select="*/*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-parentnode')">
<xsl:value-of select="parent::*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-followingnode')">
<xsl:value-of select="following-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-precedingnode')">
<xsl:value-of select="preceding-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="test">
<!--<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">-->
<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:variable>
我查看了 saxon 文档并尝试了各种解决方案,但其中 none 有效。可以这样做吗?
应该补充说我使用的是 Saxon 9.1.0.8 - 抱歉,XSLT 还是新手
如下更改 xpath。
$compose//*[name()=$sel or contains(@outputclass,$sel)]
最终代码如下
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[name()=$sel or contains(@outputclass,$sel)]" group-by="@*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
尝试
select="if ($from='oclass-attribute') then $compose//*[contains(@outputclass,$sel)] else $compose//*[name()=$sel]"
并对 group-by
属性使用相同的方法:
group-by="if ($from='oclass-attribute') then @*[name()=$group] else child::*[name()=$group]"
首先,saxon:evaluate()
不是完成这项工作的正确工具。
现在,为什么它不起作用?要声明 $oSel 的值,您做了如下操作:
<xsl:value-of select="$compose//*[contains(@outputclass,$sel)]"/>
计算 select 属性中的表达式及其结果 returns。但是您随后将 $oSel 传递给 saxon:evaluate()
,它需要一个包含 XPath 表达式的字符串。我认为您正在尝试将变量绑定到表达式“$compose//*[contains(@outputclass,$sel)]”,而不是该表达式的计算结果。为此,您必须编写
<xsl:value-of select="'$compose//*[contains(@outputclass,$sel)]'"/>
注意额外的引号;但这现在会失败,因为传递给 saxon:evaluate()
的表达式不能显式使用 $compose
等变量(有一种传递参数的机制,但你真的不想去那里)。
在 XSLT 3.0 中 saxon:evaluate
被标准指令 xsl:evaluate
取代;但你也不想要那个。
此处使用的正确机制是高阶函数。
在 XSLT 3.0 中你可以写
<xsl:for-each-group select="$compose//*[$predicate(.)]" group-by="$grouping-key(.)">
其中$predicate
和$grouping-key
是绑定到用户定义函数的变量。您可以使用如下逻辑绑定这些变量:
<xsl:variable name="predicate" as="function(element()) as xs:boolean">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:sequence select="function($n){contains($n/@outputclass,$sel)}"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:sequence select="function($n){name($n)=$sel}"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
我正在尝试使用 saxon:evaluate 来减少 xsl 函数中的重复代码。 但是我尝试的任何事情 returns 都是错误的。
这是一段重复代码。
<!--Select by outputclass and group by attribute-->
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[contains(@outputclass,$sel)]" group-by="@*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
<!--Select by node and group by child node-->
<xsl:when test="$from='node-childnode'">
<xsl:for-each-group select="$compose//*[name()=$sel]" group-by="child::*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
我想要的是传递一个参数,指示选择的是元素名称还是输出类属性。
然后是另一个参数,指示分组依据:属性或父节点、子节点、节点之后或之前的节点。
我试过的如下:
<xsl:variable name="oSel">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:value-of select="$compose//*[contains(@outputclass,$sel)]"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:value-of select="$compose//*[name()=$sel]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="oGroup">
<xsl:choose>
<xsl:when test="ends-with($from,'-attribute')">
<xsl:value-of select="*/@*[local-name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-childnode')">
<xsl:value-of select="*/*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-parentnode')">
<xsl:value-of select="parent::*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-followingnode')">
<xsl:value-of select="following-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-precedingnode')">
<xsl:value-of select="preceding-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="test">
<!--<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">-->
<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:variable>
我查看了 saxon 文档并尝试了各种解决方案,但其中 none 有效。可以这样做吗?
应该补充说我使用的是 Saxon 9.1.0.8 - 抱歉,XSLT 还是新手
如下更改 xpath。
$compose//*[name()=$sel or contains(@outputclass,$sel)]
最终代码如下
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[name()=$sel or contains(@outputclass,$sel)]" group-by="@*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
尝试
select="if ($from='oclass-attribute') then $compose//*[contains(@outputclass,$sel)] else $compose//*[name()=$sel]"
并对 group-by
属性使用相同的方法:
group-by="if ($from='oclass-attribute') then @*[name()=$group] else child::*[name()=$group]"
首先,saxon:evaluate()
不是完成这项工作的正确工具。
现在,为什么它不起作用?要声明 $oSel 的值,您做了如下操作:
<xsl:value-of select="$compose//*[contains(@outputclass,$sel)]"/>
计算 select 属性中的表达式及其结果 returns。但是您随后将 $oSel 传递给 saxon:evaluate()
,它需要一个包含 XPath 表达式的字符串。我认为您正在尝试将变量绑定到表达式“$compose//*[contains(@outputclass,$sel)]”,而不是该表达式的计算结果。为此,您必须编写
<xsl:value-of select="'$compose//*[contains(@outputclass,$sel)]'"/>
注意额外的引号;但这现在会失败,因为传递给 saxon:evaluate()
的表达式不能显式使用 $compose
等变量(有一种传递参数的机制,但你真的不想去那里)。
在 XSLT 3.0 中 saxon:evaluate
被标准指令 xsl:evaluate
取代;但你也不想要那个。
此处使用的正确机制是高阶函数。
在 XSLT 3.0 中你可以写
<xsl:for-each-group select="$compose//*[$predicate(.)]" group-by="$grouping-key(.)">
其中$predicate
和$grouping-key
是绑定到用户定义函数的变量。您可以使用如下逻辑绑定这些变量:
<xsl:variable name="predicate" as="function(element()) as xs:boolean">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:sequence select="function($n){contains($n/@outputclass,$sel)}"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:sequence select="function($n){name($n)=$sel}"/>
</xsl:when>
</xsl:choose>
</xsl:variable>