【问题标题】:Xalan (XSLT) translate method translating more than it shouldXalan (XSLT) 翻译方法的翻译超出了应有的范围
【发布时间】:2023-04-02 22:02:02
【问题描述】:

我似乎对 Xalan 的翻译方法有疑问。 我有以下代码:

translate(translate(string(name),'<sup>',''),'</sup>','')

这用于从字符串(名称)中删除<sup></sup>。不幸的是,当我这样做时,它似乎也从名称中删除了 s、u 和 p。 所以像sony Braiva <sup>tm</sup> 这样的名字变成ony bravia tm

提前感谢您的帮助:)

【问题讨论】:

  • 好问题 (+1)。请参阅我的答案以获得解释以及两个 XSLT 1.0 解决方案和一个 XSLT 2.0 解决方案。

标签: xml xslt xalan


【解决方案1】:

因为你说 translate() 函数成功删除了<sup></sup>,所以我假设<sup> 不是XML 文档中的元素,而是编码为文本。

translate() 函数被定义为替换单个字符,一般不适用于字符串替换,当字符串长度大于 1 时。

可以在 XSLT 中编写和使用通用字符串替换递归模板/函数。

XSLT 2.0 程序员可以使用标准的 XPath 2.0 函数 replace()。

在您的特定情况下,这可能就足够了:

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

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

 <xsl:template match="text()">
  <xsl:variable name="vPart1" select=
   "substring-before(., '&lt;sup>')"/>

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

  <xsl:variable name="vPart2" select=
   "substring-before(substring-after(., '&lt;sup>'),
                     '&lt;/sup>'
                     )"/>

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

  <xsl:variable name="vPart3" select=
   "substring-after(., '&lt;/sup>')"/>

  <xsl:value-of select="$vPart3"/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时

<name>
 <![CDATA[sony Braiva <sup>tm</sup> xxx]]>
</name>

产生想要的结果

<name>
sony Braiva tm xxx
</name>

或者,这里是成熟的递归模板解决方案:

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

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

 <xsl:template match="text()">
  <xsl:variable name="vFirstReplacement">
      <xsl:call-template name="replace">
       <xsl:with-param name="pText" select="."/>
       <xsl:with-param name="pPattern"
         select="'&lt;sup>'"/>
       <xsl:with-param name="pReplacement" select="''"/>
      </xsl:call-template>
  </xsl:variable>

  <xsl:call-template name="replace">
   <xsl:with-param name="pText"
        select="$vFirstReplacement"/>
   <xsl:with-param name="pPattern"
     select="'&lt;/sup>'"/>
   <xsl:with-param name="pReplacement" select="''"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="replace">
  <xsl:param name="pText"/>
  <xsl:param name="pPattern"/>
  <xsl:param name="pReplacement"/>

  <xsl:choose>
   <xsl:when test="not(contains($pText, $pPattern))">
    <xsl:value-of select="$pText"/>
   </xsl:when>
   <xsl:otherwise>
     <xsl:value-of select=
      "substring-before($pText, $pPattern)"/>

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

     <xsl:call-template name="replace">
      <xsl:with-param name="pText" select=
       "substring-after($pText, $pPattern)"/>
      <xsl:with-param name="pPattern"
           select="$pPattern"/>
      <xsl:with-param name="pReplacement"
           select="$pReplacement"/>
     </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于此 XML 文档时

<name>
 <![CDATA[sony Braiva <sup>tm</sup> xxx]]>
</name>

产生了想要的正确结果:

<name>
 sony Braiva tm xxx
</name>

最后,这里是 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:strip-space elements="*"/>

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

 <xsl:template match="text()">
  <xsl:value-of select=
   "replace(
            replace(., '&lt;sup>', ''),
            '&lt;/sup>',
            ''
            )
   "/>
 </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 谢谢。这有帮助!哇...严重的说不出话来。非常感谢。
【解决方案2】:

tl;dr 版本:如果可以避免的话,不要将 html 或 xml 作为字符串操作。在 XSLT 中执行。

我假设你所拥有的是一些元素包含类似的东西

<name>Sony Braiva <sup>tm</sup></name>

所以看起来您已经在 XSLT 中获得了一个已解析的 XML 文档。然后,您转身尝试使用字符串操作来拉出一些标签。这是个坏主意;请参阅this question 了解匹配标签。 XSLT 正是用于这种操作,所以使用它! (如果我的假设是错误的,并且 tm 是实体化的或在 CDATA 部分或其他任何东西中,那我猜是不同的。)

所以,首先。如果您想去除名称中的所有标签,只留下文本,您可以这样做

<xsl:value-of select="name" />

这会给:

Sony Braiva tm

另一方面,如果您想去除所有 sup 标记及其内容,您将首先在其他地方定义一个匹配 sup 的模板(并对任何您想要删除的内容执行相同操作,例如脚本标签、img 标签等):

<xsl:template match="sup" /> <!-- replace sup with nothing -->

然后你就可以申请了

<xsl:apply-templates select="name" />

如果你真的想要,你甚至可以做这样的事情并用一个漂亮的 unicode 符号替换那个 HTML。将其置于不同的模式并使用该模式消除所有其他标签可能是个好主意。

<xsl:template match="sup" mode="mangle-name">
  <xsl:if test="'tm' = string(.)">
  &#8482;
  </xsl:if>
</xsl:template>

<!-- Later, somewhere else: -->
<xsl:apply-templates select="name" mode="mangle-name" />

关于所有这些的免责声明:它是标准 XSLT(甚至可能是 1.0),但我只在在线 Saxon 解析器中尝试过它,而不是在 Xalan 中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-20
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-20
    • 1970-01-01
    • 2017-03-11
    相关资源
    最近更新 更多