XSLT模板匹配使用变量

时间:2018-12-15 04:48:31

标签: xml xslt xpath saxon

在我的源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,以向XPaths中指定的所有节点添加属性。下面的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>

我想使用相同的样式表来与模板的match属性中的变量一起使用-

<?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 :(得分:0)

好吧,我希望这很明显为什么您的方法行不通:在XSLT 3.0中,您可以在匹配模式中使用变量,但是变量的值必须是一组节点,而不是用于选择这些节点的XPath表达式。

要评估以字符串形式提供的XPath表达式,您需要xsl:evaluate

<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许可证作为圣诞节礼物,那么另一种方法是将您的解决方案实现为两个转换的序列:首先生成包含所需匹配模式的样式表,然后执行该样式表。

LATER

实际上,对于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最终定稿之前就出现了。

答案 1 :(得分:0)

这是另一种相当原始的方法,但是可以一次性完成:

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