【问题标题】:General substitution over all XML (XSLT)所有 XML 的通用替换 (XSLT)
【发布时间】:2010-08-27 16:46:09
【问题描述】:

基本上,我现在正在做的是运行 XSLT,然后在 Visual Studio 中打开结果并查找并替换一个单词 - 对于本示例,我想将“bar”的所有实例更改为“我的酒吧”。可以假定“bar”的所有实例都在文本元素中,如下所示:

<someElement>bar.whatever</someElement>

这将被转换为:

<someElement>myBar.whatever</someElement>

但需要注意的是,我还在运行其他转换,例如重命名或移动元素。有什么方法可以将这两个操作(转换和查找和替换)组合到一个 XSLT 中?这是否可以用于多个查找和替换?

感谢所有帮助,并提前致谢!

编辑:来自 cmets

我应该指定我是 确实使用 XSLT 2.0。我正在阅读 那篇文章并试图弄清楚 我将如何使用 replace()

【问题讨论】:

  • adam_0:好的做法是问另一个问题。此外,在您的新问题中提供输入样本和所需的输出。
  • 我创建了一个新问题:stackoverflow.com/questions/3620298/… 并删除了第二个问题

标签: xml xslt replace xslt-2.0


【解决方案1】:

XSLT 1.0 没有强大的文本搜索和替换功能。您可以使用containssubstring-beforesubstring-after 进行处理,但您必须使用递归模板来处理您尝试修复的字符串多次出现子字符串的情况。

假设您移动和重命名元素的变换是恒等变换的变体,这可行:

<xsl:template match="text()">
  <xsl:call-template name="replace">
    <xsl:with-param name="text" select="."/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="replace">
  <xsl:param name="text"/>
  <xsl:choose>
    <xsl:when test="contains($text, 'bar')">
      <xsl:call-template name="replace">
        <xsl:with-param name="text" select="concat(
                        substring-before($text, 'bar'), 
                        'myBar',
                        substring-after($text, 'bar'))"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$text"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

请注意,在任何使用value-of 复制元素值的地方,都需要使用apply-templates;改变这个:

<xsl:template match="someElement">
   <renamedElement>
      <xsl:value-of select="."/>
   <renamedElement>
</xsl:template>

进入这个:

<xsl:template match="someElement">
   <renamedElement>
      <xsl:apply-templates select="text()"/>
   <renamedElement>
</xsl:template>

进行多次替换有点棘手。您必须扩展replace 模板以获取searchForreplaceWith 参数,这很容易,然后在text() 模板中执行此操作:

<xsl:variable name="pass1">
   <xsl:call-template name="replace">
      <xsl:with-param name="text" select="."/>
      <xsl:with-param name="searchFor">bar</xsl:with-param>
      <xsl:with-param name="replaceWith">myBar</xsl:with-param>
   </xsl:call-template>
</xsl:variable>

<xsl:variable name="pass2">
   <xsl:call-template name="replace">
      <xsl:with-param name="text" select="."/>
      <xsl:with-param name="searchFor">bar</xsl:with-param>
      <xsl:with-param name="replaceWith">myBar</xsl:with-param>
   </xsl:call-template>
</xsl:variable>

<xsl:value-of select="$pass2"/>

在支持在文本节点上使用正则表达式的 XSLT 2.0 中,这要容易得多。您仍然创建一个匹配text() 的模板,但它只是调用了replace。如果您有幸能够使用 XSLT 2.0,请参阅 this article 以获取大量信息。

【讨论】:

  • @Robert Rossney:我认为你在没有的地方增加了一些复杂性。如果 OP 要详细说明所需的匹配,那么您可以说 XSLT 1.0 解决方案会有多复杂。另外,为什么不建议只匹配文本节点?
  • OP 说他想替换“'bar' 的所有实例”,而不是“出现在文本节点开头的任何 'bar' 实例”。如果您可以编写一个更简单的 XSLT 1.0 模板,可以将barbarian 更改为myBarmyBarian,我非常希望看到它。我不明白你的第二个问题。
  • @Robert Rossney:OP 还为输入样本提供了所需的输出。 ;) 我可以轻松地重写我的答案以解决您的barbarianmyBarmyBarian 转换,只需在我的答案中添加一些说明。只有当文本节点变得巨大时才会出现复杂性,因为需要使用 DVC 来避免 stackoverflow。对于第二个问题的解释,您可以看到我的答案...
  • 好吧,让我澄清一下:我非常希望看到一个非递归的 XSLT 1.0 模板,它可以替换文本节点中出现的 n 个子字符串。如果您可以通过在答案中向模板添加一些说明来做到这一点,我会印象深刻。
  • 我应该指定我确实在使用 XSLT 2.0。我正在阅读那篇文章并试图弄清楚我将如何使用 replace()
【解决方案2】:

编辑:现在已经澄清,OP 希望每次出现的 'bar' 都替换为 'myBar',这个 XSLT 1.0 样式表:

<xsl:stylesheet version="1.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="text()" name="text">
        <xsl:param name="pString" select="."/>
        <xsl:choose>
            <xsl:when test="contains($pString,'bar')">
                <xsl:value-of 
                select="concat(substring-before($pString,'bar'),'myBar')"/>
                <xsl:call-template name="text">
                    <xsl:with-param name="pString" 
                    select="substring-after($pString,'bar')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

有输入:

<someElement>barbarian</someElement>

输出:

<someElement>myBarmyBarian</someElement>

现在,XSLT 2.0 样式表:

<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="text()">
        <xsl:value-of select="replace(.,'bar','myBar')"/>
    </xsl:template>
</xsl:stylesheet>

注意:文本节点模式匹配。

【讨论】:

    【解决方案3】:

    您可以使用 alejandro 建议的 Identity Transform 模式,并替换为 exslt str:replace

    如果您的处理器不支持此功能,您可以在同一页面上找到作为 XSLT 模板的实现。

    要启用 exslt,只需在样式表中使用适当的命名空间,如 here 所述

    一些额外的指针: 您可以阅读有关here: 和here 模式的更多信息。

    仅使用第一个模板将原样复制源到目标。然后,您可以简单地为每个要进行更具体格式化的元素添加额外的模板,在您的情况下,匹配“text()”的模板,我猜它会进行替换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-29
      • 2021-11-09
      • 1970-01-01
      • 2018-04-18
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 2015-08-26
      相关资源
      最近更新 更多