Reputation: 10882
I have two XML documents which I need to merge. Every element has a defined ID. If an element is unique in both documents -> will be added to the result, if not -> attributes will be merged.
main.xml
<main>
<el id="1" attr1="value1" />
<el id="2" attr2="value2" default-attr="def" />
</main>
snippet.xml
<main>
<el id="2" attr2="new value2" new-attr="some value" />
<el id="3" attr3="value3" />
</main>
result.xml
<main>
<el id="1" attr1="value1" />
<el id="2" attr2="new value2" default-attr="def" new-attr="some value" />
<el id="3" attr3="value3" />
</main>
Attributes in the el[@id=2] are merged and values are overwritten from the snippet.xml.
I have tried this:
merge.xlst
<?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:param name="snippetDoc" select="document(snippet.xml)" />
<xsl:template match="@* | node() | comment()">
<xsl:copy>
<xsl:apply-templates select="@* | node() | comment()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="el">
<xsl:copy>
<!-- how to distinguish between @ids of two documents? -->
<xsl:copy-of select="$snippetDoc/main/el/[@id = @id]/@*" />
<xsl:apply-templates select="@*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But it needs to be able to distinguish between the same attribute in two document. What's more, this doesn't copy the unique elements from the snippet.xml.
Thanks for any help!
Upvotes: 1
Views: 596
Reputation: 70618
The expression you are looking for is this....
<xsl:copy-of select="$snippetDoc/main/el[@id=current()/@id]/@*" />
You should also put this after <xsl:apply-templates select="@*" />
so that you can take advantage of the fact that new attributes will overwrite any existing attributes written if they have the same name.
To add in elements in the snippet that do no have a matching element in the main document, you will need to do this in a template matching the main
element.
<xsl:template match="main">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:copy-of select="$snippetDoc/main/el[not(@id=current()/el/@id)]" />
</xsl:copy>
</xsl:template>
Try this XSLT
<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:param name="snippetDoc" select="document('snippet.xml')" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="el">
<xsl:copy>
<!-- how to distinguish between @ids of two documents? -->
<xsl:apply-templates select="@*" />
<xsl:copy-of select="$snippetDoc/main/el[@id=current()/@id]/@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="main">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:copy-of select="$snippetDoc/main/el[not(@id=current()/el/@id)]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Do note that node()
is actually short-hand for *|text()|comment()|processing-instruction()
so doing node()|comment()
is actually unnecessary.
Upvotes: 1