I need to merge two XML files using XSLT. The transformation takes place on an XML file that contains a list of XML files to be merged.
list.xml
<?xml version="1.0" encoding="UTF-8" ?>
<files>
<file>..srcmainresourcestestOne.xml</file>
<file>..srcmainresourcestestTwo.xml</file>
</files>
These are my two templates to merge:
<xsl:template name="merge_nodes">
<xsl:param name="fnNewDeept"/>
<xsl:param name="snNewDeept"/>
<xsl:for-each select="$fnNewDeept">
<xsl:call-template name="merge_node">
<xsl:with-param name="first-node" select="$fnNewDeept"/>
<xsl:with-param name="second-node" select="$snNewDeept"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="merge_node">
<xsl:param name="first-node" />
<xsl:param name="second-node" />
<xsl:element name="{name(current())}">
<xsl:for-each select="$second-node/@*">
<xsl:copy/>
</xsl:for-each>
<xsl:if test="$first-node = '' and not(boolean($first-node/*) and boolean($second-node/*))">
<xsl:value-of select="$second-node"/>
</xsl:if>
<xsl:for-each select="$first-node/@*">
<xsl:copy/>
</xsl:for-each>
<xsl:if test="not(boolean($first-node/*) and boolean($second-node/*))">
<xsl:value-of select="$first-node"/>
</xsl:if>
<xsl:choose>
<xsl:when test="boolean($first-node/*) or boolean($second-node/*)">
<xsl:choose>
<xsl:when test="boolean($first-node/*/*)">
<xsl:call-template name="merge_nodes">
<xsl:with-param name="fnNewDeept" select="$first-node/*"/>
<xsl:with-param name="snNewDeept" select="$second-node/*"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
2. Value: <xsl:value-of select="current()/*"/>
2. Current: <xsl:value-of select="name(current()/*)"/>
2. First: <xsl:value-of select="name($first-node/*)"/>
2. Second: <xsl:value-of select="name($second-node/*)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
1. Value: <xsl:value-of select="current()"/>
1. Current: <xsl:value-of select="name(current())"/>
1. First: <xsl:value-of select="name($first-node)"/>
1. Second: <xsl:value-of select="name($second-node)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
- Value, Current, First and Second only for debug reasons.
and my two XMLs:
<?xml version="1.0" encoding="UTF-8" ?>
<first x="1">
<second param="wt" second="true">
<third>abc</third>
<third>def</third>
</second>
<fourth>
<fifth x="1">hij</fifth>
<fifth>klm</fifth>
</fourth>
<sixth>qrs</sixth>
</first>
2.
<?xml version="1.0" encoding="UTF-8" ?>
<first y="2">
<second param="123" second="false">
<third>asd</third>
<third>def</third>
</second>
<fourth>
<fifth y="2">tuv</fifth>
<fifth>wxy</fifth>
</fourth>
<sixth>678</sixth>
<sixth>910</sixth>
</first>
I expect the first file to be preferred, so that the second file is merged into the first. Duplicate elements should not occur.
Expected Output:
<?xml version="1.0" encoding="UTF-8" ?>
<first x="1" y="2">
<second param="wt" second="true">
<third>abc</third>
<third>def</third>
<third>asd</third>
</second>
<fourth>
<fifth x="1">hij</fifth>
<fifth>klm</fifth>
<fifth y="2">tuv</fifth>
<fifth>wxy</fifth>
</fourth>
<sixth>qrs</sixth>
<sixth>678</sixth>
<sixth>910</sixth>
</first>
Output i got:
<?xml version="1.0" encoding="windows-1252"?><first y="2" x="1">
<second param="wt" second="true">
2. Value: abc
2. Current: third
2. First: third
2. Second: third</second>
<fourth param="wt" second="true">
2. Value: hij
2. Current: fifth
2. First: third
2. Second: third</fourth>
<sixth param="wt" second="true">
2. Value:
2. Current:
2. First: third
2. Second: third</sixth>
</first>
I don’t know how to run along both trees at the same time so I can copy the elements. Anybody got any ideas?
I only can use Apaches XALAN. I use the newest Version 2.7.2.
Edit: Since there has already been a misunderstanding. The transformation must be applicable to similar XML files, that’s the big problem.
2
Answers
I have tried to understand your requirements and parts of Oliver Becker’s implementation explained in http://web.archive.org/web/20160502222322/http://www2.informatik.hu-berlin.de/~obecker/XSLT/#merge and shown in http://web.archive.org/web/20160502194427/http://www2.informatik.hu-berlin.de/~obecker/XSLT/merge/merge.xslt.html by adapting the comparison of elements nodes (elements with no child elements are considered equivalent if name, namespace and content match, elements with complex content (i.e. element children) are considered equivalent if they have the same name and namespace) and by adapting the merging of elements to copy attributes from the second document (
$node2/@*
are copied first as I think you want to have the attributes in the first doc to have preference).I have tested that online at https://xsltfiddle.liberty-development.net/nc4NzQq with Saxon 9.8 HE, but only to be able to share the code and the result in an executable manner as an XSLT 2 or 3 processor easily allows me to inline the second document. So I have
which then gives the result
which is close to your wanted result, I am not sure if you can have mixed contents (i.e. text and element children mixed), if not, I think using
xsl:strip-space
andxsl:output indent="yes"
, as done in https://xsltfiddle.liberty-development.net/nc4NzQq/1, give you a clean resultOn the other hand, as I had the second sample inline where white space is stripped, it might suffice to assume that does not happen for the normal case of using the
document
function and then, simulating that in https://xsltfiddle.liberty-development.net/nc4NzQq/2 withxml:space="preserve"
on the inline sample, the resultalso looks promising. So try to adapt respectively change it to use the
with
parameter and thedocument
function, as in the original, then you might get the wanted result, at least for the two samples you have shown. It is hard to tell whether it is a generic solution as I think the whole idea of merging depends very much on a clear specification of how to compare nodes exactly.