Reputation: 7775
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
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
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