Find and replace entity in xslt

北城余情 提交于 2019-12-08 13:26:46

问题


i need to find and replace an entity in xslt. i need to do this without using template match.

suppose my xml like this,

<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>

i need to change &apos; into &rsquo; so output should be like this,

<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>

i used the replace method by using template match p but after that i can not able to do any operation on other tags like blockfixed etc,. so pls suggest simple xslt to do this.

i used the following 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>

thanks in advance.


回答1:


The problem is with your template matching the p element

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

In particular, you use of mode. Although you are selecting all child nodes of the p element, you have only provided a matching template for text() nodes with a mode of replace. This means all other elements won't get picked out, and so blockFixed is ignored.

Instead, remove the above template matching p from your XSLT, and replace the following line:

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

With this one

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

This assumes you building upon the identity transform in your XSLT. Here is the abridged version of the 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>



回答2:


You mention the replace method which is only available in XSLT and XPath 2.0 so I assume that you use an XSLT 2.0 processor? And are you aware that XSLT operates on a data model with plain text nodes with Unicode string values so entity reference are not modeled in that data model. And to enforce a character in the transformation result to be output as an entity reference instead of as a Unicode character you would need to XSLT 2.0 character maps.

So what you can do is process text nodes with analyze-string e.g.

<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>

to output an apos element with that right quote character , then you would need to add a character map that ensures such a character is replaced with its entity reference.

And I realize I used a template to solve that, in spite of your request. But all you need to incorporate my suggestion into a more complex stylesheet is doing apply-templates in a template for p elements, so maybe you can do that.



来源:https://stackoverflow.com/questions/11503644/find-and-replace-entity-in-xslt

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!