如何根据XSLT参数创建匹配的模板

时间:2014-11-17 17:21:26

标签: xslt xpath

我正在尝试创建一个标准用途的XSLT,它将根据用户提供的XPATH表达式作为XSLT参数执行给定任务。

也就是说,我需要这样的东西:

<xsl:template match="$paramContainingXPATH">
  <!-- perform the task on the node(s) in the given xpath -->
</xsl:template>

例如,假设我有一些XML:

<xml>
  <nodeA>whatever</nodeA>
  <nodeB>whatever</nodeB>
  <nodeC>whatever</nodeC>
  <nodeD>whatever</nodeD>
  <nodeE>whatever</nodeE>
</xml>

XSLT只需要转换与提供的XPATH表达式匹配的节点。因此,如果xslt参数是“/ xml / nodeC”,它将处理nodeC。如果xslt参数是“* [local-name()='nodeC'或local-name()='nodeE']”,则它处理nodeC和nodeE。

这应该适用于绝对任何 XML消息。也就是说,XSLT不能直接了解XML的内容。因此,它可以是原始XML或SOAP信封。

我猜我可能需要抓取与xpath匹配的所有节点,然后循环调用命名模板,并使用标准身份模板用于所有其他节点。

感谢所有建议。

3 个答案:

答案 0 :(得分:3)

如果你确实需要XSLT 1.0或2.0的那个功能,那么我认为你应该考虑编写一个样式表,用XPath表达式获取该字符串参数,然后简单地生成第二个样式表的代码,其中XPath表达式用作匹配模式和静态包含身份模板等其他所需模板。动态XPath评估仅在XSLT 3.0或早期版本中作为专有扩展机制提供。

答案 1 :(得分:1)

您无法使用参数匹配模板 - 但可以遍历树并将每个节点的路径与给定路径进行比较。这是一个简单的例子:

XSLT 1.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:param name="path" select="'/world/America/USA/California'"/>

<xsl:template match="/">
    <root>
        <xsl:apply-templates select="*"/>
    </root>
</xsl:template>

<xsl:template match="*">
    <xsl:variable name="path-to-me">
        <xsl:for-each select="ancestor-or-self::node()">
            <xsl:value-of select="name()" />
            <xsl:if test="position()!=last()">
                <xsl:text>/</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable> 
    <xsl:if test="$path=$path-to-me">
        <xsl:call-template name="action"/>
    </xsl:if>
    <xsl:apply-templates select="*"/>
</xsl:template>

<xsl:template name="action">
    <return>
        <xsl:value-of select="." />
    </return>
</xsl:template>

</xsl:stylesheet>

应用于稍微更雄心勃勃的测试输入

<world>
    <Europe>
        <Germany>1</Germany>
        <France>2</France>
        <Italy>3</Italy>
    </Europe>
    <America>
        <USA>
            <NewYork>4</NewYork>
            <California>5</California>
        </USA>
        <Canada>6</Canada>
    </America>
</world>

结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <return>5</return>
</root>

通过将累积路径作为递归模板的参数传递,可以提高效率,这样每个节点只需要将自己的名称添加到链中。

注意

  1. 给定的路径必须是绝对的;

  2. 谓词(包括位置谓词)和属性未在此实现。他们可能会有更多的努力;

  3. 忽略命名空间(我不知道如何将XPath作为参数传递并包含名称空间)。

  4. 如果您的处理器支持evaluate()扩展功能,您可以放弃计算的文本路径并测试交叉点。


    编辑:

    以下是使用EXSLT dyn的示例:evaluate()和set:intersection():

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dyn="http://exslt.org/dynamic"
    xmlns:set="http://exslt.org/sets"
    extension-element-prefixes="dyn set">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:param name="path" select="'/world/America/USA/California'"/>
    <xsl:variable name="path-set" select="dyn:evaluate($path)" />
    
    <xsl:template match="/">
        <root>
            <xsl:apply-templates select="*"/>
        </root>
    </xsl:template>
    
    <xsl:template match="*">
        <xsl:if test="set:intersection(. , $path-set)">
            <xsl:call-template name="action"/>
        </xsl:if>
        <xsl:apply-templates select="*"/>
    </xsl:template>
    
    <xsl:template name="action">
        <return>
            <xsl:value-of select="." />
        </return>
    </xsl:template>
    
    </xsl:stylesheet>
    

    请注意,这也适用于以下路径:

    /world/America/USA/*[2]

    //California

    以及文本比较方法无法容纳的许多其他内容。

答案 2 :(得分:0)

我将元素名称作为参数发送到XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
    <xsl:output method="xml"/>
    <xsl:param name="user"/>
    <xsl:template match="/">
        <xsl:call-template name="generic" />
    </xsl:template>
    <xsl:template name="generic">
        <count><xsl:value-of select="count(.//*[local-name()=$user])"/></count>
    </xsl:template>
</xsl:stylesheet>

我希望这可以提供帮助!