RE: [xsl] Replace values based on external files

Subject: RE: [xsl] Replace values based on external files
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Fri, 14 Jan 2005 09:54:54 -0000
This needs an extension function such as dyn:evaluate, because you need to
evaluate XPath expressions that aren't hard-coded in the stylesheet. You
probably want the variant of this in Saxon called saxon:evaluate-node(),
which is designed specifically to handle XPath expressions in source XML
documents, and takes the namespace context for the XPath expression from the
element node in which it is contained.

Implementing this efficiently is not easy, because you don't want to copy
the whole source document each time you find a node whose value needs to be
replaced. In effect you want to build a list of nodes that need to be
changed, together with their replacement values, and then you want to do a
single scan of the source document testing each node to see if it is in this
list. Unfortunately the intermediate data structure - a mapping from nodes
to replacement values - is not easily represented in the XPath data model.
I'd try doing it using generate-id(), something like this:

<xsl:variable name="source" select="/">

<xsl:variable name="replacements">
  <xsl:for-each select="replace">
    <replace>
      <xsl:for-each select="$source/saxon:evaluate-node(current()/xpath)">
        <node><xsl:value-of select="generate-id(.)"/></node>
      </xsl:for-each>
      <xsl:copy-of select="with"/>
    </replace>
  </xsl:for-each>
</xsl:variable>

<xsl:key name="n" match="node" use="."/>

<xsl:template match="*">
  <xsl:copy>
  <xsl:apply-templates select="@*"/>
  <xsl:variable name="replacement" 
      select="$replacements/key('n', generate-id(current()))"/>
  <xsl:choose>
    <xsl:when test="$replacement">
      <xsl:value-of select="$replacement/../with"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates/>
    </
  </
</

<xsl:template match="@*">
  <xsl:variable name="replacement" 
      select="$replacements/key('n', generate-id(current()))"/>
  <xsl:choose>
    <xsl:when test="$replacement">
      <xsl:attribute name="{name(.)}" namespace="{namespace-uri(.)}" 
                     select="$replacement/../with"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy/>
    </
  </
</

Another way of tackling this problem would be by generating a stylesheet to
do it. That would remove the need for extension functions.

Michael Kay
http://www.saxonica.com/

> -----Original Message-----
> From: Robert Soesemann [mailto:rsoesemann@xxxxxxxxxxx] 
> Sent: 14 January 2005 09:28
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: [xsl] Replace values based on external files
> 
> Hello,
> 
> I need to implement a XSL that will replace text values in file1.xml
> based on replacement information in file2.xml.
> 
> file2.xml provides an XPath that points to an attribute / 
> element value
> and a replacement value. The XML has the form of:
> 
> <mapping>
>     <!-- should replace @attribute1 value that are 'foo' of 
> all element1
> elements in file1.xml with 'bar'-->
>     <replace>  
>         <xpath>//element1/@attribute1[.='foo']</xpath>
>         <with>bar</with>
>     </replace>
>     <replace>
>         ...                    <!-- other replacement operations -->
>     </replace>
>     ...
> </mapping>
> 
> Is there an easy way to dynamically generate replacement 
> instructions in
> my XSL. I thought of extracting replacement information from file2.xml
> with the document function.
> 
> 
> Any help is much welcome.
> 
> Robert

Current Thread