roland
roland

Reputation: 7775

XSLT: How to use a variable as lookup to update node

Here is my source.xml:

<entries>
    <entry path="/folder/abc.txt">
        <a>abc</a>
        <b>baz</b>
    </entry>
    <entry path="/other/def.txt">
        <a>foo</a>
        <b>bar</b>
    </entry>    
</entries>

My XSLT looks like:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
    <xsl:variable name="lookup">
        <pair>
            <key>/folder/abc.txt</key>
            <value>/other/folder/abc.txt</value>
        </pair>
        <pair>
            <key>/other/def.txt</key>
            <value>/other/folder/misc/def.txt</value>
        </pair>
    </xsl:variable>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Therefore I can reproduce my source xml. What I want is to update path using the variable lookup (where path should match key to return value).

The final output would be:

<entries>
    <entry path="/other/folder/abc.txt">
        <a>abc</a>
        <b>baz</b>
    </entry>
    <entry path="/other/folder/misc/def.txt">
        <a>foo</a>
        <b>bar</b>
    </entry>    
</entries>

The latest requirement is to preserve the indentation exactly as it is.

How can I achieve that?

Thanks in advance.

Upvotes: 1

Views: 2007

Answers (2)

forty-two
forty-two

Reputation: 12817

You can put the lookup in a separate file and make it easier with a litte rearranging:

lookup.xml:

<entries>
    <entry path="/folder/abc.txt">/other/folder/abc.txt</entry>
    <entry path="/other/def.txt">/other/folder/misc/def.txt</entry>
</entries>

your 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="entry/@path">
        <xsl:variable name="oldpath" select="." />
        <xsl:attribute name="path"><xsl:value-of
            select="document('lookup.xml')/entries/entry[@path = $oldpath]" /></xsl:attribute>
    </xsl:template>

</xsl:stylesheet>

Note that the solution will not preserve the original value if there is no match.

Upvotes: 1

Ben L
Ben L

Reputation: 1312

The following XSL template should do it. It uses an EXSLT extension function to convert your variable into a node-set. (Warning: If there is no match for path in your variable, then the path will be removed!)

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

  <xsl:variable name="lookup">
    <pair>
      <key>/folder/abc.txt</key>
      <value>/other/folder/abc.txt</value>
    </pair>
    <pair>
      <key>/other/def.txt</key>
      <value>/other/folder/misc/def.txt</value>
    </pair>
  </xsl:variable>

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

  <xsl:template match="entry">
    <xsl:copy>
      <xsl:attribute name="path">
        <xsl:call-template name="replace">
          <xsl:with-param name="input" select="@path"/>
        </xsl:call-template>
      </xsl:attribute>
      <xsl:apply-templates select="*"/>
    </xsl:copy>
  </xsl:template>

  <!-- This template looks through each pair in lookup, finds the one whose
       key matches the input and returns the corresponding value. If none
       match, nothing will be returned. -->    
  <xsl:template name="replace">
    <xsl:param name="input"/>
    <xsl:for-each select="exslt:node-set($lookup)/pair">
        <xsl:if test="key = $input">
          <xsl:value-of select="value"/>
        </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

You can see it working here.

Upvotes: 2

Related Questions