【问题标题】:Find and replace entity in xslt在 xslt 中查找和替换实体
【发布时间】:2012-07-16 11:49:01
【问题描述】:

我需要在 xslt 中查找并替换一个实体。我需要在不使用模板匹配的情况下执行此操作。

假设我的 xml 是这样的,

<root>
  <p>Master&apos;s</p>
  <p><blockFixed type="quotation"><p>let&apos;s</p></blockFixed></p>
   <p>student&apos;s<featureFixed id=1/></p>
   <p><blockFixed type="quotation"><p>nurse&apos;s</p></blockFixed></p>
   <p>Master&apos;s</p>
</root>

我需要将&amp;apos; 更改为&amp;rsquo; 所以输出应该是这样的,

<root>
  <p>Master<apos>&rsquo;</apos>s</p>
  <p><blockFixed type="quotation"><p>let<apos>&rsquo;</apos>s</p></blockFixed></p>
   <p>student<apos>&rsquo;</apos>s<featureFixed id=1/><\p>
   <p><blockFixed type="quotation"><p>nurse<apos>&rsquo;</apos>s</p></blockFixed></p>
   <p>Master&apos;s</p>
</root>

我通过模板匹配p 使用替换方法,但之后我无法对blockfixed 等其他标签进行任何操作。所以请建议使用简单的 xslt 来执行此操作。

我使用了以下xslt,

  <xsl:template match="p">
    <xsl:copy>
   <xsl:apply-templates select="current()/node()" mode="replace"/>
    </xsl:copy>
  </xsl:template>
     <xsl:template match="text()" mode="replace">
<xsl:call-template name="replace-string">
  <xsl:with-param name="text" select="."/>
  <xsl:with-param name="from">&apos;</xsl:with-param>
  <xsl:with-param name="to" select="'&rsquo;'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
    <xsl:when test="contains($text, $from)">
    <xsl:variable name="before" select="substring-before($text, $from)"/>
    <xsl:variable name="after" select="substring-after($text, $from)"/>
    <xsl:variable name="prefix" select="concat($before, $to)"/>
    <xsl:copy-of select="$before"/>
    <Apos>
      <xsl:copy-of select="$to"/>
    </Apos>
    <xsl:call-template name="replace-string">
      <xsl:with-param name="text" select="$after"/>
      <xsl:with-param name="from" select="$from"/>
      <xsl:with-param name="to" select="$to"/>
    </xsl:call-template>
   </xsl:when>
  <xsl:otherwise>
    <xsl:copy-of select="$text"/>
  </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

提前致谢。

【问题讨论】:

  • 在没有模板匹配的 XSLT 中很少发生。为什么会有这个限制?然后,您说您正在使用模板匹配(匹配p)。请详细说明。
  • 除非您使用 DOE(禁用输出转义),否则这在 XSLT 1.0 中是不可能的——但此功能不是强制性的,并且并非所有 XSLT 1.0 处理器都实现了它。强烈推荐使用 XSLT 2.0。

标签: xml xpath xslt-1.0


【解决方案1】:

问题在于您的模板与 p 元素匹配

<xsl:template match="p">
  <xsl:copy>
    <xsl:apply-templates select="current()/node()" mode="replace"/>
  </xsl:copy>
</xsl:template> 

特别是您使用了模式。尽管您选择了 p 元素的所有子节点,但您只为 text() 节点提供了一个匹配模板,其模式为 replace。这意味着不会挑选出所有其他元素,因此 blockFixed 会被忽略。

相反,从您的 XSLT 中删除与 p 匹配的上述模板,并替换以下行:

<xsl:template match="text()" mode="replace"> 

有了这个

<xsl:template match="p/text()">

这假设您在 XSLT 中建立身份转换。这是 XSLT 的精简版

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

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

  <xsl:template match="p/text()">
      <xsl:call-template name="replace-string">
          <xsl:with-param name="text" select="."/>
          <xsl:with-param name="from">'</xsl:with-param>
          <xsl:with-param name="to" select="'&amp;rsquo;'"/>
      </xsl:call-template>
  </xsl:template>

  <xsl:template name="replace-string">
      <xsl:param name="text"/>
      <xsl:param name="from"/>
      <xsl:param name="to"/>
      <xsl:choose>
          <xsl:when test="contains($text, $from)">
            <xsl:variable name="before" select="substring-before($text, $from)"/>
            <xsl:variable name="after" select="substring-after($text, $from)"/>
            <xsl:variable name="prefix" select="concat($before, $to)"/>
            <xsl:copy-of select="$before"/>
            <Apos>
                <xsl:value-of select="$to" disable-output-escaping="yes"/>
            </Apos>
            <xsl:call-template name="replace-string">
                <xsl:with-param name="text" select="$after"/>
                <xsl:with-param name="from" select="$from"/>
                <xsl:with-param name="to" select="$to"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
</xsl:stylesheet>

【讨论】:

  • TimC:你有没有注意到,如果在输出中生成了&amp;rsqo;,那么输出就不是一个格式良好的XML 文档?如果输出方法是"xml",我认为兼容的 XSLT 处理器应该生成格式良好的 XML 文档——否则这是一个错误。也许@MichaelKay 可以阐明这一点?
【解决方案2】:

您提到了仅在 XSLT 和 XPath 2.0 中可用的 replace 方法,所以我假设您使用 XSLT 2.0 处理器?您是否知道 XSLT 在具有 Unicode 字符串值的纯文本节点的数据模型上运行,因此实体引用不在该数据模型中建模。并且要强制将转换结果中的字符作为实体引用而不是作为 Unicode 字符输出,您需要 XSLT 2.0 字符映射。

所以你可以做的是处理带有分析字符串的文本节点,例如

<xsl:template match="p//text()">
  <xsl:analyze-string select="." regex="'">
    <xsl:matching-substring>
       <apos>&#8217;</apos>
    </xsl:matching-substring>
    <xsl:non-matching-substring>
       <xsl:value-of select="."/>
    </xsl:non-matching-substring>
  </xsl:analyze-string>
</xsl:template>

要输出带有右引号字符apos 元素,则需要添加一个字符映射,以确保将此类字符替换为其实体引用。

我意识到我使用了一个模板来解决这个问题,尽管您提出了要求。但是,将我的建议合并到更复杂的样式表中所需要做的就是在 p 元素的模板中执行 apply-templates,所以也许您可以这样做。

【讨论】:

  • 对不起,我使用的是 xslt 1.0 处理器。这是在 xslt 1.0 上工作的吗?
  • 不,抱歉,analyze-string 是 XSLT 2.0 中的新功能。和人物地图一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多