Reputation: 312
I have two XML documents that need to be combined in special way.
As a simple example I'll just put both documents into one:
<?xml version="1.0" encoding="utf-8" ?>
<data>
<config1>
<option1 type="a">A</option1>
<option2 type="b">B</option2>
<option3 type="c">C</option3>
</config1>
<config2>
<option2 type="bb">BB</option2>
<option4>D</option4>
</config2>
</data>
So, i need to merge config1 and config2 to this result:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<config>
<option1 type="a">A</option1>
<option2 type="bb">BB</option2>
<option3 type="c">C</option3>
<option4>D</option4>
</config>
</data>
Transformation rules are:
i've made following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<config>
<!-- Select options from config 1. -->
<xsl:for-each select="config1/*">
<xsl:choose>
<!-- Leave option as is. -->
<xsl:when test="not(/data/config2/*[name() = name(current())])">
<xsl:copy-of select="."/>
</xsl:when>
<!-- Overwrite option. -->
<xsl:otherwise>
<xsl:copy-of select="/data/config2/*[name() = name(current())]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- Select options from config 2. -->
<xsl:for-each select="config2/*">
<!-- Append "new" option -->
<xsl:if test="not(/data/config1/*[name()=name(current())])">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</config>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It works, but looks lame.
Many XSL gurus prefer apply-templates over for-each.
Is it possible to rewrite my template in that way?
Upvotes: 0
Views: 97
Reputation: 117043
As a simple example I'll just put both documents into one:
That's not a good idea, because dealing with two documents is not the same as dealing with two node-sets in the same document. So, assuming your processed document is:
<?xml version="1.0" encoding="utf-8" ?>
<data>
<config1>
<option1 type="a">A</option1>
<option2 type="b">B</option2>
<option3 type="c">C</option3>
</config1>
</data>
and there is another document:
file2.xml
<?xml version="1.0" encoding="utf-8" ?>
<data>
<config2>
<option2 type="bb">BB</option2>
<option4>D</option4>
</config2>
</data>
you could use the following stylesheet:
XSLT 1.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="config2" select="document('file2.xml')/data/config2" />
<xsl:template match="/">
<data>
<config>
<xsl:apply-templates select="data/config1/*[not(name()=name($config2/*))] | $config2/*"/>
</config>
</data>
</xsl:template>
<xsl:template match="data/*/*">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
to obtain this result:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<config>
<option1 type="a">A</option1>
<option3 type="c">C</option3>
<option2 type="bb">BB</option2>
<option4>D</option4>
</config>
</data>
Upvotes: 2