如何将 xslt 中的参数用作 XPath?

Posted

技术标签:

【中文标题】如何将 xslt 中的参数用作 XPath?【英文标题】:How to use a parameter in a xslt as a XPath? 【发布时间】:2011-01-16 01:48:08 【问题描述】:

我想向 xml 文档添加一个元素,并且我想将元素的路径作为参数传递。

sample.xml 文件:

<?xml version="1.0"?>
<stuff>
  <element1>
    <foo>2</foo>
<bar/>
  </element1>
  <element2>
<subelement/>
<bar/>
   </element2>
   <element1>
     <foo/>
 <bar/>
   </element1>
 </stuff>

使用:

xalan.exe -p myparam "element1" sample.xml addelement.xslt

我想要以下结果:

<?xml version="1.0"?>
<stuff>
  <element1>
    <foo>2</foo>
    <bar/>
    <addedElement/>
  </element1>
  <element2>
<subelement/>
<bar/>
   </element2>
   <element1>
     <foo/>
 <bar/>
     <addedElement/>
   </element1>
 </stuff>

我已经设法编写 addelement.xslt,在硬编码路径时它可以工作,但是当我尝试使用 我得到的匹配属性中的参数 myparam:

XPathParserException: A node test was expected.
pattern = '$myparam/*[last()]' Remaining tokens are:  ('$' 'myparam' '/' '*' '[' 'last' '(' ')' ']') (addelement.xslt, line 12, column 42)

addelement.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

<xsl:template match="element1/*[last()]">
    <xsl:copy-of select="."/>
<addedElement></addedElement>
</xsl:template>

</xsl:stylesheet>

替换硬编码路径的addelement.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:param name="myparam"/>

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

<xsl:template match="$myparam/*[last()]">
    <xsl:copy-of select="."/>
<addedElement></addedElement>
</xsl:template>

</xsl:stylesheet>

感谢您的帮助

【问题讨论】:

【参考方案1】:

我认为您不能像编写代码那样在匹配模板中使用变量/参数。即使这样也行不通

<xsl:template match="*[name()=$myparam]/*[last()]">

相反,尝试将第一个匹配模板更改为如下所示,以便参数检查在模板代码内部,而不是作为匹配语句的一部分。

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <xsl:if test="local-name() = $myparam">
            <addedElement/>
        </xsl:if>
    </xsl:copy>
</xsl:template>

【讨论】:

您的意思是变量/参数不是有效的 XPath 表达式吗?但是,我无法使其与您的解决方案一起使用。 首先,XSLT 1.0 根本不允许在匹配模式中使用变量/参数。 XSLT 2.0 确实允许这样做,但是如果您使用 Xalan,一个 XSLT 1.0 处理器,您就不能使用 XSLT 2.0 功能。其次,如果您有一个带有元素名称的字符串类型的变量或参数,那么您不能使用它来动态地构造 XPath 表达式。这不是变量/参数的工作方式,无论是在 XPath 还是在其他语言中。如果变量/参数值是字符串类型,那么您可以在任何允许字符串的地方使用它,就像 Tim 所做的那样。 谢谢 Martin,您知道任何基于命令行的 XSLT 2.0 处理器吗? Saxon 9 (saxon.sourceforge.net)、Gestalt (gestalt.sourceforge.net)、AltovaXML Tools (altova.com/altovaxml.html) 都可以在命令行中使用。 我很困惑,我可以使它与 Tim 的解决方案一起使用 Xalan 和 Saxon 9。使用 Xalan,它不起作用,因为我必须使用单引号命令行中参数的值。由于 Tim 的解决方案也适用于 Xalan,因此我认为 Tim 的解决方案与 XSLT 1.0 兼容。顺便说一句,我不确定我是否理解您的第一条评论。如果我错了,请纠正我:XSLT 2.0 允许匹配模式中的变量/参数,但它们不能用于构造 XPath 表达式?那么,如何在匹配模式中使用它们呢?【参考方案2】:

以下是使用 XSLT 1.0 的方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:param name="n" select="'element1'"/>

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

<xsl:template match="*/*[last()]">
  <xsl:choose>
    <xsl:when test="local-name(..) = $n">
      <xsl:copy-of select="."/>
      <addedElement></addedElement>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

【讨论】:

以上是关于如何将 xslt 中的参数用作 XPath?的主要内容,如果未能解决你的问题,请参考以下文章

使用 XPath 将 URL 查询字符串解析为参数映射

XPathXQuery 以及 XSLT 函数

如何使用 C# 将 XSLT 更改为使用动态 XPath?

如果没有给出参数,如何忽略添加到 XSLT 文件中的参数?

将命名空间从 java 传递给 xslt,并使用 java 中的参数作为 xslt 中的节点

XSLT/XPath 中的当前节点与上下文节点?