Реализовать динамический xpath в XSLT: для каждой группы - PullRequest
1 голос
/ 29 апреля 2019

Я пытаюсь использовать saxon: оценивать, чтобы уменьшить повторение кода в функции 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>     

То, что я хочу, - это передать параметр, определяющий, является ли он именем элемента или атрибутом outputclass.

Затем другой параметр, определяющий, что группировать: атрибут или родитель, потомок, следующий или предшествующий узел.

То, что я пробовал, ниже:

<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 9.1.0.8 - Извините, я все еще новичок в XSLT

Ответы [ 3 ]

2 голосов
/ 29 апреля 2019

Во-первых, saxon:evaluate() не подходит для работы.

Теперь, почему это не работает? Чтобы объявить значение $ oSel, вы сделали что-то вроде этого:

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

, который оценивает выражение в атрибуте select и возвращает его результат. Но затем вы передаете $ oSel в saxon:evaluate(), что ожидает строку, содержащую выражение XPath. Я думаю, что вы пытаетесь связать переменную с выражением «$ compose // * [содержит (@ 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 голос
/ 29 апреля 2019

Попробуйте

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]"
1 голос
/ 29 апреля 2019

Измените 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>

...