Соответствие шаблона XSLT с использованием переменной - PullRequest
0 голосов
/ 15 декабря 2018

В моем исходном XML у меня есть элемент, который содержит список выражений Xpath, указывающих на разные узлы в одном и том же XML.Вот пример, где Xpath расположены в /root/Properties[@name='changed'] -

<root xmlns="http://www.example.org">
<main>
    <child1>123</child1>
    <child2>456</child2>
    <subChildren>
        <subChild1>321</subChild1>
        <subChild2>644</subChild2>
    </subChildren>
</main>
<Properties name="changed">/t:root/t:main/t:child2|/t:root/t:main/t:subChildren/t:subChild1</Properties>
</root>

Я пишу XSLT, чтобы добавить атрибут ко всем узлам, указанным в XPath.XLST ниже выполняет свою работу, однако обратите внимание, что xpath жестко закодированы в значении соответствия второго шаблона -

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:t="http://www.example.org" version='2.0'>

<xsl:variable name="xpaths" select="/t:root/t:Properties[@name='changed']" />

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
</xsl:template>

<xsl:template match='/t:root/t:main/t:child2|/t:root/t:main/t:subChildren/t:subChild1'>
    <xsl:call-template name="addChanged" />
</xsl:template>

<xsl:template name="addChanged">
    <xsl:copy>
        <xsl:attribute name="changed">true</xsl:attribute>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Я хочу, чтобы та же таблица стилей работала с переменной в атрибуте соответствия шаблона, например, так-

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:t="http://www.example.org" version='2.0'>

<xsl:variable name="xpaths" select="/t:root/t:Properties[@name='changed']" />

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
</xsl:template>

<xsl:template match='$xpaths'>
    <xsl:call-template name="addChanged" />
</xsl:template>

<xsl:template name="addChanged">
    <xsl:copy>
        <xsl:attribute name="changed">true</xsl:attribute>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Как мне это сделать?Приведенная выше таблица стилей не соответствует узлам в шаблоне.Я использую Saxon HE 9.6.

Ответы [ 2 ]

0 голосов
/ 16 декабря 2018

Вот еще один подход, который довольно примитивен, но выполняется за один проход:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.example.org">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="paths" select="tokenize(root/Properties[@name='changed'], '\|')" />

<xsl:template match="*">
    <xsl:variable name="path-to-me">
        <xsl:for-each select="ancestor-or-self::*">
            <xsl:value-of select="concat('/t:', name())"/>
        </xsl:for-each>    
    </xsl:variable> 
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:if test="$path-to-me = $paths">
            <xsl:attribute name="changed">true</xsl:attribute>
        </xsl:if>
        <xsl:apply-templates select="*"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Демо : http://xsltransform.hikmatu.com/6qVRKvL

0 голосов
/ 15 декабря 2018

Что ж, надеюсь, очевидно, что ваш подход не работает: в XSLT 3.0 вы можете использовать переменную в шаблоне соответствия, но значение переменной должно быть набором узлов, а не выражением XPath для выбора этих узлов.

Чтобы оценить выражение XPath, представленное в виде строки, вам нужно xsl: define

<xsl:variable name="path-to-changed-nodes"
  select="/t:root/t:Properties[@name='changed']" as="xs:string"/>

<xsl:variable name="changed-nodes" as="node()*">
  <xsl:evaluate xpath="$path-to-changed-nodes"
                context-item="/"/>
</xsl:variable>

<xsl:template match="$changed-nodes">
  ...
</xsl:template>

В Saxon для инструкции xsl:evaluate требуется Saxon-PE или выше.Если вы не можете убедить кого-либо выдать вам лицензию Saxon-PE в качестве рождественского подарка, альтернативный подход заключается в реализации вашего решения в виде последовательности двух преобразований: сначала создайте таблицу стилей, содержащую необходимые шаблоны соответствия, а затем выполните эту таблицу стилей.

ПОЗЖЕ

Фактически в XSLT 3.0 существует более простая альтернатива генерации таблицы стилей в качестве источника XML;Вы можете использовать статические параметры и теневые переменные (хотя я не проверял это).

Начните с теневой переменной для шаблона совпадения:

<xsl:template _match="{$pattern}">...

Затем определите статическую переменную $ pattern:

<xsl:variable name="pattern" static="yes" 
     select="string($doc//t:root/t:Properties[@name='changed'])"/>

Затем объявите параметр $doc:

<xsl:param name="doc" static="yes" as="document-node()"/>

и укажите значение для статического параметра doc при компиляции таблицы стилей.

У меня естьне знаю, сработает ли это с Saxon-HE 9.6 - вероятно, нет, поскольку это было сделано до завершения XSLT 3.0.

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