Как я могу использовать переменную XSLT, содержащую шаблон, в выражении сопоставления с шаблоном

Я бы хотел сделать

<xsl:variable name="myPattern" select="node1|node2"/>
<xsl:template match="$myPattern">
    ...
</xsl:template>
<xsl:template  match="/">
    ...
    <xsl:for-each select="distinct-values(//$myPattern/name/text()">
        ...
    </xsl:for-each>
</xsl:template>

Я пробовал это с XSLT версии 2.0 и 3.0, но безрезультатно. Какие-нибудь намеки?

Причина: шаблон немного сложнее, и я хотел бы использовать его в нескольких местах, а не только в этом совпадении.

РЕДАКТИРОВАТЬ:

На данный момент я решил свою проблему, приняв тот факт, что переменная содержит не строку / шаблон, а узлы результата. Если я изменю его на

<xsl:variable name="myNodes" select="//(node1|node2)"/>
<xsl:template match="$myNodes">
    ...
</xsl:template>
<xsl:template  match="/">
    ...
    <xsl:for-each select="distinct-values($myNodes/name/text()">
        ...
    </xsl:for-each>
</xsl:template>

работает нормально.

Мне все еще интересно, почему невозможно просто сохранить строку в переменной и использовать ее везде, где разрешены буквальные строки.


person mdr    schedule 07.09.2016    source источник
comment
XSLT 3.0 позволяет использовать, например, match="$xyz" и объясняет, что $ xyz соответствует любому узлу, который присутствует в значении переменной $ xyz. Таким образом, вам нужно будет показать нам минимальный, но полный образец XML, XSLT, ожидаемых результатов, результатов или ошибок, которые вы получаете, вместе с информацией об используемом процессоре XSLT, чтобы мы могли определить, что не так. Очевидно, я ожидал бы, что ваше объявление переменной будет скорее выглядеть, например, <xsl:variable name="//node1 | //node2"/>, если только элементы, которые вы ищете, действительно не являются корневыми.   -  person Martin Honnen    schedule 07.09.2016
comment
@MartinHonnen: Вы правы. Я хотел, чтобы вопрос был кратким, чтобы другие могли сэкономить время при поиске ответа. Я думаю, что ваш комментарий даже лучше, чем ваш ответ, поскольку я решил его решить следующим образом: <xsl:variable name="myNodes" select="//(node1|node2)"/>.   -  person mdr    schedule 07.09.2016
comment
@mdr Вам придется использовать какую-то функцию evaluate() для преобразования строки в выражение XPath (если вы это имеете в виду).   -  person michael.hor257k    schedule 07.09.2016
comment
@ michael.hor257k: Думаю, это то, что я ищу. Вы можете показать мне, как?   -  person mdr    schedule 07.09.2016
comment
@mdr, см. редактирование моего ответа, если вы хотите, чтобы текстовая замена использовала атрибуты тени.   -  person Martin Honnen    schedule 07.09.2016


Ответы (2)


Что касается текстовой замены, с XSLT 3.0 вы можете использовать статический параметр со строковым значением, а затем так называемые теневые атрибуты (https://www.w3.org/TR/xslt-30/#shadow-атрибуты):

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

    <xsl:param name="myPattern" static="yes" as="xs:string" select="'node1|node2'"/>

    <xsl:template _match="{$myPattern}">
        <matched name="{node-name()}">
            <xsl:apply-templates/>
        </matched>
    </xsl:template>

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:for-each _select="distinct-values(//{$myPattern}/text())">
                <value>
                    <xsl:value-of select="."/>
                </value>
            </xsl:for-each>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Это трансформирует

<root>
    <node1>a</node1>
    <node2>1</node2>
    <node1>a</node1>
</root>

в

<root><value>a</value><value>1</value>
        <matched name="node1">a</matched>
        <matched name="node2">1</matched>
        <matched name="node1">a</matched>
</root>

В XSLT 3.0 вы можете использовать ссылку на переменную или параметр для шаблона match шаблона, но это не текстовая замена, которая происходит, а скорее «$ xyz соответствует любому узлу, который присутствует в значении переменной $ xyz» (https://www.w3.org/TR/xslt-30/#pattern-examples).

Итак, если XSLT

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:param name="delete" select="//*[contains-token(@class, 'foo')]"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="$delete"/>

</xsl:stylesheet>

и ввод XML

<html>
    <head>
        <title>test</title>
    </head>
    <body>
        <p class="foobar bar">Paragraph 1.</p>
        <p class="foo bar">Paragraph 2.</p>
        <p class="bar">Paragraph 3.</p>
        <p class="foo">Paragraph 4.</p>
    </body>
</html>

соответствующий процессор XSLT 3.0, такой как Saxon 9.7 EE, выводит

<html>
    <head>
        <title>test</title>
    </head>
    <body>
        <p class="foobar bar">Paragraph 1.</p>

        <p class="bar">Paragraph 3.</p>

    </body>
</html>
person Martin Honnen    schedule 07.09.2016

Я бы посоветовал использовать функцию, а не переменную:

<xsl:function name="_:myPattern" as="xs:boolean">
  <xsl:param name="node" as="node()"/>
  <xsl:sequence select="self::node1() | self::node2()"/>
</xsl:function>

<xsl:template match="node()[_:myPattern(.)]">
 ...
</xsl:template>
person Michael Kay    schedule 07.09.2016
comment
Это похоже на то, что я задумал: оценка шаблона, где бы ни использовалась переменная (или, скорее, функция). Но: Ого! Это не то, что вы бы назвали легко обслуживаемым для случайного пользователя / программиста XSLT. Поэтому я буду придерживаться более простого (см. Мое РЕДАКТИРОВАНИЕ). Но я многому научился. - person mdr; 07.09.2016
comment
Спасибо. Я не мог заставить это работать, не уверен, что это опечатка? Я пробовал exists ($ node / (self :: node1 | self :: node2)), который, похоже, работает. Или, возможно, $ node / (self :: node1 | self :: node2) с element() ? в качестве возвращаемого типа. - person dave; 05.05.2017
comment
Не возвращая весь поток обратно в свой мозг, я подозреваю, что намеревался <xsl:sequence select="$node[self::node1() | self::node2()]"/> - person Michael Kay; 06.05.2017