在XSLT中实现动态xpath:for-each-group

问题描述 投票:1回答:3

我正在尝试使用saxon:evaluate来减少xsl函数中重复的代码。但是我尝试的任何东西都会返回错误

这是重复代码的一部分。

<!--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文档并尝试了各种解决方案,但它们都没有工作。是否有可能做到这一点?

应该补充说我使用的是Saxon 9.1.0.8 - 抱歉,XSLT还是新手

xslt xpath foreach group-by saxon
3个回答
2
投票

首先,saxon:evaluate()不适合这项工作。

现在,为什么不工作?要声明$ oSel的值,你做了类似这样的事情:

<xsl:value-of select="$compose//*[contains(@outputclass,$sel)]"/>

它评估select属性中的表达式并返回其结果。但是你要将$ 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>

1
投票

更改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>


1
投票

尝试

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]"
© www.soinside.com 2019 - 2024. All rights reserved.