Using xsl:evaluate and the path() function to merge data from two documents with the same structure

On Stackoverflow there was a question to merge two documents with the same XML structure by copying text data from elements in a secondary input document to empty elements in the main input. I think this can be nicely done by using the path() function on empty elements together with the xsl:evaluate instruction (that is new in XSLT 3.0) to simply select and copy the text data from the secondary input document

<xsl:template match="*[not(has-children())]">
<xsl:copy>
<div class="merged">
<xsl:evaluate context-item="$doc2" xpath="path() || '/text()'"></xsl:evaluate>
</div>
</xsl:copy>
</xsl:template>

Full gist is
<?xml version="1.0" encoding="UTF-8"?>
<root>
<content>
<text-block>
<descriptionHead>
Some description text for the text block head.
</descriptionHead>
<description>
Some description text block text.
</description>
</text-block>
<shortDescription>
<textHead>
Example text for the short description head.
</textHead>
<textBody>
Example text for the short description text body.
</textBody>
</shortDescription>
<longDescription>
<textHead>
Example text for the long description head.
</textHead>
<textBody>
Example text for the short description text body.
</textBody>
</longDescription>
</content>
</root>
view raw doc2.xml hosted with ❤ by GitHub
<?xml version="1.0" encoding="UTF-8"?>
<root>
<content>
<text-block>
<descriptionHead>
Some text 1.
</descriptionHead>
<description>
Some text 2.
</description>
</text-block>
<shortDescription>
<textHead></textHead>
<textBody></textBody>
</shortDescription>
<longDescription>
<textHead>
Some text 3.
</textHead>
<textBody></textBody>
</longDescription>
</content>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:param name="doc2-uri" as="xs:string" select="'doc2.xml'"/>
<xsl:param name="doc2" select="doc($doc2-uri)"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="*[not(has-children())]">
<xsl:copy>
<div class="merged">
<xsl:evaluate context-item="$doc2" xpath="path() || '/text()'"></xsl:evaluate>
</div>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?><root>
<content>
<text-block>
<descriptionHead>
Some text 1.
</descriptionHead>
<description>
Some text 2.
</description>
</text-block>
<shortDescription>
<textHead><div class="merged">
Example text for the short description head.
</div></textHead>
<textBody><div class="merged">
Example text for the short description text body.
</div></textBody>
</shortDescription>
<longDescription>
<textHead>
Some text 3.
</textHead>
<textBody><div class="merged">
Example text for the short description text body.
</div></textBody>
</longDescription>
</content>
</root>
view raw result.xml hosted with ❤ by GitHub

The posted stylesheet works fine with Saxon 9.7 and later editions supporting xsl:evaluate (PE and EE) and with current versions of Altova XMLSpy/Raptor.


Comments

Popular posts from this blog

Using accumulators to number items in a streamable way

Extracting sub trees of a document using snapshot()

Fun(ctional programming) with fold-left and transform