如何在Xslt中用另一个节点名称替换节点名称?

3

非常抱歉,问题的措辞不够清晰。我尝试解释一下我的意图。 基本上,我从搜索中得到了Xml格式的输出,在这个Xml中有一个节点如下:

<FIELD NAME="body">
  Somebody named 
  <key>Doris</key> 
  and 
  <key>Arnie</key> 
</FIELD>

简而言之,我需要的是将“<key>”替换为“<strong>”,即突出显示搜索命中项(关键节点值是用户搜索的内容)。在Xslt中,我不知道用户从哪里搜索,除了查询Xml -> FIELD[@name='body']/key。
现在我有一些疯狂的代码,将提取搜索词(“Doris”)前面的任何内容,但这仅适用于一个搜索词。我们需要它处理多个搜索词。我们使用的代码如下:
  <xsl:template name="highlighter">
    <xsl:param name="text"/>
    <xsl:param name="what"/>

    <xsl:choose>
      <xsl:when test="contains($text, $what) and string-length($what) &gt; 0">
        <xsl:variable name="before" select="substring-before($text, $what)"/>
        <xsl:variable name="after" select="substring-after($text, $what)"/>
        <xsl:variable name="real-before" select="substring($text, 1, string-length($before))"/>
        <xsl:variable name="real-what" select="substring($text, string-length($before) + 1, string-length($what))"/>
        <xsl:variable name="real-after" select="substring($text, string-length($before) + string-length($what) + 1)"/>
        <xsl:value-of select="$real-before"/>

        <strong>
          <xsl:value-of select="$real-what"/>
        </strong>

        <xsl:call-template name="highlighter">
          <xsl:with-param name="text" select="$real-after"/>
          <xsl:with-param name="what" select="$what"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

我一直在尝试使用不同的搜索词多次调用此代码,但我不知道如何将上一个调用到模板的输出作为下一个调用的输入。在代码中,它应该是这样的:
string body = doc.SelectSingleNode("FIELD[@NAME='body']");
NodeCollection nodes = doc.SelectNodes("FIELD[@NAME='body']/key");
foreach (var node in nodes) {
    body = hightlighter(body, node.InnerText);   
}

到目前为止,我还无法在XSLT中做到这样的事情,但我还是个新手,所以... ;)

编辑:为了澄清一下;我要求的输出是这样的:

Somebody named <strong>Doris</strong> and <strong>Arnie</strong>
3个回答

11

在这种情况下最好的做法是从输入递归地复制节点到输出,并覆盖您想要特殊处理的节点。关键思想是文本字符串也是可以复制的节点。以下是一个示例:

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

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

这看起来像是Chris Dali的答案... 如果我使用"<xsl:apply-templates select="FIELD[@NAME='body']/key" />"来应用模板,我只会得到加粗的“KEY”节点的值,这不是我想要的... 我需要从“FIELD”节点获取所有内容。 - noocyte
尝试仅选择FIELD[@NAME='body'],甚至是FIELD或/。 - Rich
你真是个传奇! :) 但是你的Xslt中有一个错别字;apply-templates不允许使用“match”,我相信应该是“select”。当我使用“FIELD[NAME='body']”时它起作用了。 :) 谢谢!! - noocyte
抱歉,我已经修复了拼写错误。 - Rich
1
不用谢。请参考我在评论中对Chris版本的进一步解释。 - Rich

2

这应该可以满足您的需求。它使用了应用模板而不是调用模板,更多地采用函数式的方式来解决这个问题。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <!-- Template to match your 'key' and replace with strong -->
    <xsl:template match="FIELD[@name='body']/key">
        <strong><xsl:apply-templates select="@*|node()"/></strong>
    </xsl:template>

    <!-- Template to match all nodes, copy them and then apply templates to children. -->
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

嗯...我该如何“调用”第一个模板?或许我应该提一下,我已经有一个相当大的XSLT文件了,我需要在匹配父节点为“FIELD”的模板中进行替换。 - noocyte
第二个模板匹配文档中的所有节点,并简单地生成它们的副本。第一个模板匹配您要查找的特定字段/键,并用<strong>替换它。这种模式通常用于使输出XML文档成为原始文档的副本。 - Chris Dail
对不起,我完全不理解你最后的评论...我的意思是,我能理解这些词,但对我来说毫无意义...(我是个新手,请多包涵) - noocyte
当您在节点或属性上应用模板时,XSLT处理器将寻找最匹配的最具体的内容。除了处理键的情况外,在所有情况下,最具体的匹配将是第二个模板,它只将当前节点编码到输出中,然后递归地将模板应用于其所有子节点或属性。如果没有任何名称为“body”的字段内部有键,则这将仅将整个输入文档复制到输出。但是,当处理器遇到键时,它会在输出中创建一个新的“strong”元素,然后移动到键的... - Rich
将子元素递归地遍历,将符合条件的“key”元素转换为“strong”元素,并将它们添加到输出中。最终结果是输入中所有适当的“key”元素都被转换为输出中的“strong”元素,而其他所有内容都保持不变(包括文本节点)。 - Rich
我觉得我有点理解了...谢谢你的详细评论,非常感激。 - noocyte

1
为什么不能用“STRONG”元素替换“KEY”元素呢?最好不要过于强制地思考这个问题。
<xsl:template match="FIELD[@NAME='body']">
  <xsl:apply-templates/>
<xsl:template>

<xsl:template match="key">
  <strong>
  <xsl:apply-templates/>
  <strong>
</xsl:template>

<xsl:template match="text()">
  <xsl:copy-of select="."/>
</xsl:template>

还是我理解错了你的意思?


嗯...我该如何“调用”第一个模板?我已经尝试过使用:“<xsl:apply-templates select="FIELD[@NAME='body']" />”,但似乎不起作用... - noocyte
只是为了澄清一下,我要的输出是这样的: 有人叫 <strong>Doris</strong> 和 <strong>Arnie</strong> - noocyte
模板应该匹配,除非您更改了默认的模板行为:样式表应该递归XML文件,直到找到节点。您的XML文件是否有命名空间?您是否在样式表中正确绑定了命名空间? - xcut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接