XSLT 1.0 - 条件节点分配

rpg*_*rpg 5 xslt xpath

使用纯XSLT 1.0,我如何有条件地分配节点.我正在尝试这样的事情,但它不起作用.

<xsl:variable name="topcall" select="//topcall"/>
<xsl:variable name="focusedcall" select="//focusedcall" />

<xsl:variable name="firstcall" select="$topcall | $focusedcall"/>
Run Code Online (Sandbox Code Playgroud)

对于变量firstcall,我正在进行条件节点选择.如果有一个topcall然后分配给firstcall,其他人分配firstcallfocusedcall.

Lar*_*rsH 5

这应该工作:

<xsl:variable name="firstcall" select="$topcall[$topcall] |
                                       $focusedcall[not($topcall)]" />
Run Code Online (Sandbox Code Playgroud)

换句话说,选择$topcall如果$topcall节点集不为空; $focusedcall如果$topcallnodeset为空.

关于"它可以是5-6个节点"的重新更新:

鉴于可能有5-6个替代品,即除了$ topcall和$ focusedcall之外还有3-4个...

最简单的解决方案是使用<xsl:choose>:

<xsl:variable name="firstcall">
  <xsl:choose>
    <xsl:when test="$topcall">    <xsl:copy-of select="$topcall" /></xsl:when>
    <xsl:when test="$focusedcall"><xsl:copy-of select="$focusedcall" /></xsl:when>
    <xsl:when test="$thiscall">   <xsl:copy-of select="$thiscall" /></xsl:when>
    <xsl:otherwise>               <xsl:copy-of select="$thatcall" /></xsl:otherwise>
  </xsl:choose>
</xsl:variable>
Run Code Online (Sandbox Code Playgroud)

但是,在XSLT 1.0中,这会将所选结果的输出转换为结果树片段(RTF:基本上是一个冻结的XML子树).之后,您将无法使用任何重要的XPath表达式$firstcall从中选择内容.如果您需要$firstcall稍后进行XPath选择,例如select="$firstcall[1]",您可以选择一些选项......

  1. 将这些选择放入<xsl:when><xsl:otherwise>将它们发生在数据转换为RTF之前.要么,
  2. 考虑node-set()扩展,它将RTF转换为节点集,因此您可以从中进行正常的XPath选择.此扩展在大多数XSLT处理器中都可用,但不是全部.要么,
  3. 考虑使用XSLT 2.0,其中RTF根本不是问题.实际上,在XPath 2.0中,如果需要,可以将正常的if/then/else条件放在XPath表达式中.
  4. 使用嵌套谓词在XPath 1.0中实现它

:

select="$topcall[$topcall] |
        ($focusedcall[$focusedcall] | $thiscall[not($focusedcall)])[not($topcall)]"
Run Code Online (Sandbox Code Playgroud)

并根据需要继续筑巢.换句话说,在这里我将XPath表达式用于上面的两个替代方案,并替换了$ focusedcall

($focusedcall[$focusedcall] | $thiscall[not($focusedcall)])
Run Code Online (Sandbox Code Playgroud)

下一次迭代,你将用$替换$ thiscall

($thiscall[$thiscall] | $thatcall[not($thiscall)])
Run Code Online (Sandbox Code Playgroud)

等等

当然这变得难以阅读,并且容易出错,所以除非其他人不可行,否则我不会选择此选项.


Dim*_*hev 2

I. XSLT 1.0 解决方案这个简短(30 行)、简单且参数化的转换适用于任意数量的节点类型/名称:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pRatedCalls">
   <call type="topcall"/>
   <call type="focusedcall"/>
   <call type="normalcall"/>
 </xsl:param>

 <xsl:variable name="vRatedCalls" select=
  "document('')/*/xsl:param[@name='pRatedCalls']/*"/>

 <xsl:variable name="vDoc" select="/"/>

 <xsl:variable name="vpresentCallNames">
  <xsl:for-each select="$vRatedCalls">
   <xsl:value-of select=
   "name($vDoc//*[name()=current()/@type][1])"/>
   <xsl:text> </xsl:text>
  </xsl:for-each>
 </xsl:variable>

 <xsl:template match="/">
  <xsl:copy-of select=
   "//*[name()
       =
        substring-before(normalize-space($vpresentCallNames),' ')]"/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当应用于此 XML 文档时(请注意文档顺序与参数中指定的优先级不一致pRatedCalls):

<t>
 <normalcall/>
 <focusedcall/>
 <topcall/>
</t>
Run Code Online (Sandbox Code Playgroud)

产生完全想要的正确结果

<topcall/>
Run Code Online (Sandbox Code Playgroud)

当相同的转换应用于以下 XML 文档时

<t>
 <normalcall/>
 <focusedcall/>
</t>
Run Code Online (Sandbox Code Playgroud)

再次产生想要的正确结果

<focusedcall/>
Run Code Online (Sandbox Code Playgroud)

解释

  1. 要搜索的节点的名称(根据需要搜索多个节点并按优先级顺序)由名为 的全局(通常是外部指定的)参数指定$pRatedCalls

  2. 在变量体内,$vpresentCallNames我们生成一个以空格分隔的元素名称列表,这些元素名称既指定为元素$pRatedCalls`type参数的属性值,也是 XML 文档中元素的名称。callin the

  3. 最后,我们确定此空格分隔列表中的第一个此类名称,并选择文档中具有此名称的所有元素。

二. XSLT 2.0 解决方案

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pRatedCalls" select=
  "'topcall', 'focusedcall', 'normalcall'"/>

 <xsl:template match="/">
  <xsl:sequence select=
   "//*
     [name()=$pRatedCalls
                [. = current()//*/name()]
                                        [1]
     ]"/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)