开发者

General substitution over all XML (XSLT)

Basically, what I'm doing right now is running an XSLT, then opening the result in Visual Studio and doing a find and replace for one word - for this example, I want to change all instances of "bar" to "myBar". All instances of "bar" may be assumed to be in text elements like so:

<someElement>bar.whatever</someElement>

This would be transformed to:

<someElement>myBar.whatever</someElement>

But the caveat to this is that I'm also running other transformations, such as renaming or moving the element. Is there any way that I can combine these two operations (the transform and the find and replace) into one XSLT? Is this possible for multiple find-and-replaces?

All help is appreciated and thanks in advance!

Edit: F开发者_开发技巧rom comments

I should have specified that I AM indeed using XSLT 2.0. I'm reading that article and trying to figure out how I would use replace()


XSLT 1.0 doesn't have robust text search-and-replace. You can gin something up that uses contains, substring-before, and substring-after, but you have to use a recursive template to handle the case where the string you're trying to fix has multiple occurrences of the substring.

This works, assuming that your transform that moves and renames elements is a variant of the identity transform:

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

Note that anywhere you're copying the value of an element using value-of, you need to be using apply-templates; change this:

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

into this:

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

Doing multiple replacements is a bit trickier. You have to extend the replace template to take a searchFor and replaceWith arguments, which is easy enough, and then do this in the text() template:

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

In XSLT 2.0, which supports using regular expressions on text nodes, this is a lot easier. You still create a template that matches text(), but it just has a call to replace. See this article for plenty of information, if you're fortunate enough to be able to use XSLT 2.0.


EDIT: Now that has been clarified that the OP wants every occurrence of 'bar' replaced by 'myBar',this XSLT 1.0 stylesheet:

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

With input:

<someElement>barbarian</someElement>

Output:

<someElement>myBarmyBarian</someElement>

Now, XSLT 2.0 stylesheet:

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

Note: text nodes pattern matching.


You can use the Identity Transform pattern as suggested by alejandro and to the replacement with exslt str:replace

If your processor does not support this, you can find an implementation as an XSLT template on the same page.

To enable exslt, just use the appropriate namespace in your stylesheet as described here

Some additional pointers: You can read more about the pattern here: and here.

Using the first template only will copy the source to the destination unchanged. Then you can simply add additional templates for each element you want to do more specific formatting, in your case one that matches "text()", I guess that does the replacement.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜