【问题标题】:XSL named parameter 'with-param' using 'apply-templates'使用“应用模板”的 XSL 命名参数“with-param”
【发布时间】:2010-11-18 21:13:26
【问题描述】:

我的问题在这篇文章的底部,如果你想在完整解释之前阅读它们。

我正在使用 XSL 将 XML 文档转换为漂亮的网页,但无法正确传递变量。我定义了许多xsl:templates,并且需要将特定参数传递给其中一个。我希望我能够传递一个命名参数,该参数可能会被发送到所有xsl:templates,但只能被一个人使用,而被其他人忽略。但是,当我尝试为自己测试时(以及我对 XSL 的有限了解),我根本无法传递参数,更不用说测试它是否意外地干扰了任何其他 xsl:templates。

以下是简化的示例代码(为这个问题输入的,它可能包含一个或两个错字)。我定义了许多不同的xsl:templates 来处理 XML 中的节点,并且到目前为止一切正常。我似乎在向这些模板添加参数时遇到了问题。

XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="main.xsl"?>
<wrapperNode>
  <testNode>
    <subNode/>
  </testNode>
</wrapperNode>

main.xsl:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="test.xsl"/>
<xsl:output method="html" indent="yes"/>

<xsl:template match="/">

<html>
  <body>
      <xsl:apply-templates>
        <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param>
      </xsl:apply-templates>
  </body>
</html>

</xsl:template>
</xsl:stylesheet>

test.xsl:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="testNode">
  <xsl:param name="testParam" />
  TEST1
  <xsl:value-of select="$testParam" />
  TEST2
</xsl:template>
</xsl:stylesheet>

输出(实际):

TEST1 TEST2

输出(预期/期望):

TEST1 TEST_PARAMETER TEST2

我的问题:

  1. 是否可以发送一个命名的 我所有的参数 xsl:templates 使用 xsl:apply-templatesxsl:with-param,但选择这个 值具体由name= 内 实际的模板,以便它可以 被明确地用在一个单一的 模板并被所有其他人忽略 (即使我想添加其他, 名称不同,参数为 其他模板稍后)?

  2. 我当前的示例代码做错了什么,它似乎根本没有收到参数?

  3. 有没有更好的方法来做到这一点?

编辑:我想明确指出,由于test.xsl:testNode 模板中的其他输出,我确定它IS成功叫。 ONLY 参数部分不起作用。我并不是要浪费人们的时间来弄清楚为什么没有调用该模板。是的。

更新:在回应我最初收到的答案时,指出我编的例子并不完全正确(我的错误)并且没有很清楚地表明问题(即:正确的模板 is 被调用,但 only 参数似乎不起作用),我已经用更好的示例替换了示例。这个例子更清楚地表明testNode 模板被成功调用,但参数似乎没有被传递。在考虑这个问题的先前答案之前和之后,我已经测试了很多次。我完全被难住了,因为从我在其他地方读到的内容和人们迄今为止的建议来看,一切似乎都是正确的。

【问题讨论】:

  • 由于@Jim Garrison 拥有answer,您不能将空命名空间URI 元素作为样式表的顶部元素。您的 XSLT 处理器应该抛出错误消息。
  • 好问题,+1。有关您问题的具体答案,请参阅我的回复。
  • 我用更准确和具体的例子更新了这个问题。我很抱歉首先有一个不太完美的例子,我是在动态创建它并且放错了一些东西(比如 Jim 指出的 HTML 标签)。

标签: xslt parameters stylesheet apply-templates


【解决方案1】:

我的问题:

  1. 是否可以将命名参数发送到我所有的 xsl:templates 使用 xsl:apply-templates xsl:with-param,但选择这个值 特别是名称=在 实际模板,以便它可以 在单个模板中显式使用 并被所有其他人忽略(即使我 想添加其他的,不同的 命名,其他模板的参数 稍后)?

是的。在 XSLT 2.0 中可以使用所谓的“隧道参数”,但在 XSLT 1.0 中这是一种让一些参数到达链下的某个远程模板的方式。

另一种方法是使用全局参数,这样它们就不必通过链中的每个模板。

.2。我当前的示例代码做错了什么 似乎完全接收到参数?

原因在于您没有向我们展示的代码。或者,您在真实案例中拥有的源 XML 文档可能与此处提供的不同。我运行了提供的代码,但我根本无法重现问题——产生了所需的输出。

我的猜测是在匹配 testNode 的模板之前选择了其他模板。此模板不知道任何有关传递参数的信息,也不会将其传递给它反过来适用。因此参数根本没有传递给匹配testNode的模板。

我的猜测是,如果你替换:

  <xsl:apply-templates> 
    <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param> 
  </xsl:apply-templates> 

  <xsl:apply-templates select="testNode"> 
    <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param> 
  </xsl:apply-templates> 

你可以得到想要的输出。

此外,您可以使用 XSLT 调试器(例如 Visual Studio 中的调试器)进行跟踪,并准确查看选择了哪个模板。

.3。有没有更好的方法来做到这一点?

正如我之前所说,可以使用全局参数作为替代方案——不过我不确定这是否更好。

最后,这是我运行的无法重现您的问题的代码:

XSLT 样式表

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>

 <xsl:template match="/">
      This is text1
      <xsl:apply-templates>
        <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param>
      </xsl:apply-templates>
      This is text2
 </xsl:template>

 <xsl:template match="testNode">
  <xsl:param name="testParam" />
  <xsl:value-of select="$testParam" />
 </xsl:template>
</xsl:stylesheet>

XML 文档

<?xml-stylesheet type="text/xsl" href="main.xsl"?>
<testNode>
  <subNode/>
</testNode>

结果

  This is text1
  TEST_PARAMETER
  This is text2

更新:

OP 提供了更准确的信息,这证明了我的猜测。

现在很明显,问题是由于允许为 wrapperNode 选择元素节点的 XSLT built-in template 引起的。这个模板自然不知道任何参数,它不使用testParam 参数,也不传递这个参数。因此,内置模板中的&lt;xsl:apply-templates/&gt; 会导致选择匹配testNode 的模板,而无需向其传递任何参数。这解释了报告的行为/结果。

解决方案:解决方案是指定一个匹配wrapperNode的模板,该模板接受一个名为testParam的参数,并在应用模板时将其传递:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" indent="yes"/>

 <xsl:template match="/">
  <html>
    <body>
        <xsl:apply-templates>
          <xsl:with-param name="testParam" select="'TEST_PARAMETER'"/>
        </xsl:apply-templates>
    </body>
  </html>
 </xsl:template>

 <xsl:template match="testNode">
  <xsl:param name="testParam" />
  TEST1
  <xsl:value-of select="$testParam" />
  TEST2
 </xsl:template>

 <xsl:template match="wrapperNode">
  <xsl:param name="testParam" />

  <xsl:apply-templates>
   <xsl:with-param name="testParam" select="$testParam"/>
  </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

现在,当对提供的 XML 文档应用此转换时,会产生预期的结果

<html>
<body>
  TEST1
  TEST_PARAMETER
  TEST2
 </body>
</html>

【讨论】:

  • 感谢您在此问题上花费的时间和知识。我用一个更好、更准确的例子编辑了这个问题。正如我之前提到的,除了参数之外,其他一切都在工作。但是,在多次阅读您的答案后,一些事情引起了我的注意:XML 中testNode 上方的节点(在更新的示例中名为wrapperNode)没有与之关联的模板。您是否认为该节点默认在某处“吃掉”参数?有什么想法或建议吗?我非常感谢您的帮助和非常详细的答案。如果可以的话 +10。
  • @KevenK:是的,这正是您的问题的原因。添加一个匹配wrapperNode 的模板,该模板有一个名为xsl:paramtestParam,并且在该模板的正文中只有:&lt;xsl:apply-templates&gt;&lt;xsl:with-param name="testParam" select="$testParam"/&gt;&lt;/xsl:apply-templates&gt;
  • Dimitre:成功了。非常感谢您的帮助,我不熟悉这种行为。该模板仍在被调用,所以我认为 apply-templates 是调用它的那个。在调用我的模板之前,我没有意识到有一个默认模板正在使用该参数。谢谢。
  • @KevenK:不客气。我用最终解决方案更新了我的答案,并提供了问题的解释。
  • 这是另一个关于隧道参数的有趣帖子:[stackoverflow.com/a/13305877/1655155]
【解决方案2】:

您的&lt;html&gt;&lt;body&gt; 标记不能出现在它们在XSL 样式表中的位置。当我删除它们和结束标签并在 Oxygen/XML 中运行它时,我会得到你的“期望”输出。我认为您想将这些标签放在顶级模板中,在这种情况下,它将在 htmlbody 标签内生成输出。

您使用的哪个 XSLT 引擎没有抱怨样式表无效?

【讨论】:

  • 您可能是对的,并且 +1 表示观察,但不幸的是,这只是我为这个问题输入的示例中的一个缺陷。正如我在问题中明确提到的那样,除了参数之外,一切正常。虽然您的答案是正确的,但恐怕它对我的问题没有帮助(我想是我弄乱了这个例子的错)。不过,我感谢您的时间和精力。
【解决方案3】:

我按原样运行海报的 3 个示例,并在安装了 XML 工具的 Ubuntu 18.04 下的 Wine 上使用 Notepad++ 7.7.1 得到了这个结果。

<html><body>

  TEST1
  TEST_PARAMETER
  TEST2

</body></html>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    • 2021-01-08
    • 1970-01-01
    相关资源
    最近更新 更多