Merging XML with XSLT and PowerShell? – OK!

Combining XMl files from PowerShell – well, that’s pretty easy once you figured out how to work with

, but doing a correct & automatic merge turns out to be a quite challenging task.

Luckily there’s this: merge.xslt by (LGPL) by Oliver Becker – a XSL transformation ready accomplish this task in no time!

Let’s assume we’ve got two XML files.

FileA.xml:

<br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br />
&lt;dict&gt;<br />
	&lt;awesome id=&quot;21&quot;&gt;Saxon&lt;/awesome&gt;<br />
	&lt;awesome id=&quot;42&quot;&gt;Chocolatey&lt;/awesome&gt;</p>
<p>	&lt;someweirdtag&gt;<br />
		I want candy.<br />
	&lt;/someweirdtag&gt;<br />
&lt;/dict&gt;<br />

FileB.xml:

<br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br />
&lt;dict&gt;<br />
	&lt;awesome id=&quot;21&quot;&gt;Coffee&lt;/awesome&gt;<br />
	&lt;awesome id=&quot;42&quot;&gt;Chocolatey&lt;/awesome&gt;</p>
<p>	&lt;someweirdtag&gt;<br />
		I'm afraid of catfish.<br />
	&lt;/someweirdtag&gt;<br />
	&lt;IhaveNoMemoryOfThisPlace/&gt;<br />
&lt;/dict&gt;<br />

What we want to accomplish is a merge of these two files – and there are several different possible outcomes:
If we just combine the two files, we’d want a result like this:

<br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;dict&gt;<br />
	&lt;awesome id=&quot;21&quot;&gt;SaxonCoffee&lt;/awesome&gt;<br />
	&lt;awesome id=&quot;42&quot;&gt;Chocolatey&lt;/awesome&gt;</p>
<p>	&lt;someweirdtag&gt;<br />
		I want candy.<br />
		I'm afraid of catfish.<br />
	&lt;/someweirdtag&gt;<br />
	&lt;IhaveNoMemoryOfThisPlace/&gt;<br />
&lt;/dict&gt;<br />

In the scenario I’ve been facing I needed to combine two files, favoring the second one – so all existing elements from FileA would be overridden by the elements of FileB.

<br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;dict&gt;<br />
	&lt;awesome id=&quot;21&quot;&gt;Coffee&lt;/awesome&gt;<br />
	&lt;awesome id=&quot;42&quot;&gt;Chocolatey&lt;/awesome&gt;</p>
<p>	&lt;someweirdtag&gt;<br />
		I'm afraid of catfish.<br />
	&lt;/someweirdtag&gt;<br />
	&lt;IhaveNoMemoryOfThisPlace/&gt;<br />
&lt;/dict&gt;<br />

Adding the missing PowerShell script:

<br />
param(<br />
[Parameter(Mandatory = $True)][string]$file1,<br />
[Parameter(Mandatory = $True)][string]$file2,<br />
[Parameter(Mandatory = $True)][string]$path<br />
)</p>
<p># using only abs paths .. just to be safe<br />
$file1 = Join-Path $(Get-Location) $file1<br />
$file2 = Join-Path $(Get-Location) $file2<br />
$path = Join-Path $(Get-Location) $path</p>
<p># awesome xsl stylesheet from Oliver Becker<br />
# http://web.archive.org/web/20160502194427/http://www2.informatik.hu-berlin.de/~obecker/XSLT/merge/merge.xslt<br />
$xsltfile = Join-Path $(Get-Location) &quot;merge.xslt&quot;</p>
<p>$XsltSettings = New-Object System.Xml.Xsl.XsltSettings<br />
$XsltSettings.EnableDocumentFunction = 1</p>
<p>$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;<br />
$xslt.Load($xsltfile , $XsltSettings, $(New-Object System.Xml.XmlUrlResolver))</p>
<p>[System.Xml.Xsl.XsltArgumentList]$al = [System.Xml.Xsl.XsltArgumentList]::new()<br />
$al.AddParam(&quot;with&quot;, &quot;&quot;, $file2)<br />
$al.AddParam(&quot;replace&quot;, &quot;&quot;, &quot;true&quot;)</p>
<p>[System.Xml.XmlWriter]$xmlwriter = [System.Xml.XmlWriter]::Create($path)<br />
$xslt.Transform($file1, $al, $xmlwriter)<br />

The eagle-eyed viewer spotted a caveat: yes, this does not run on Linux, there’s no

System.Xml.Xsl

in DotNetCore/PowerShell so far, but hopefully this will change!
– this seems to no longer be true, thank’s Brian!

If you don’t have System.Xml.Xsl or PowerShell for any reason, just swap the .NETish XSL code with our all-time-favorite Saxon!

<br />
java -jar saxon9he.jar .\FileA.xml .\merge.xslt with=FileB.xml replace=true<br />

~ happy hacking!

Links:

Update: replaced $args in Code because of PowerShell 5.1 (thanks https://outofmemoryexception.wordpress.com/2016/08/05/powershell-5-1-14393/ )

6 thoughts on “Merging XML with XSLT and PowerShell? – OK!

  1. Just FYI. System.Xml.Xsl appears to now be in Powershell 6 Core and I am using it on my Mac.

  2. This is great, thanks.

    A couple of improvements:
    If you gci the file params then you get the file object, and then you can use either a relative or absolute path:
    # using only abs paths .. just to be safe
    $path = Join-Path $(Get-Location) $path
    $file1=gci $file1
    $file2=gci $file2

    You should close the xmlwriter at the end.
    $xmlwriter.close()

    1. Also, for compatibility with earlier versions of .net (4.0, maybe others):

      [System.Xml.Xsl.XsltArgumentList]$al = [System.Xml.Xsl.XsltArgumentList]::new()
      should be
      [System.Xml.Xsl.XsltArgumentList]$al = new-object System.Xml.Xsl.XsltArgumentList

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.