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:
<?xml version="1.0" encoding="utf-8"?>
<dict>
<awesome id="21">Saxon</awesome>
<awesome id="42">Chocolatey</awesome>
<someweirdtag>
I want candy.
</someweirdtag>
</dict>
FileB.xml:
<?xml version="1.0" encoding="utf-8"?>
<dict>
<awesome id="21">Coffee</awesome>
<awesome id="42">Chocolatey</awesome>
<someweirdtag>
I'm afraid of catfish.
</someweirdtag>
<IhaveNoMemoryOfThisPlace/>
</dict>
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:
<?xml version="1.0" encoding="utf-8"?><dict>
<awesome id="21">SaxonCoffee</awesome>
<awesome id="42">Chocolatey</awesome>
<someweirdtag>
I want candy.
I'm afraid of catfish.
</someweirdtag>
<IhaveNoMemoryOfThisPlace/>
</dict>
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.
<?xml version="1.0" encoding="utf-8"?><dict>
<awesome id="21">Coffee</awesome>
<awesome id="42">Chocolatey</awesome>
<someweirdtag>
I'm afraid of catfish.
</someweirdtag>
<IhaveNoMemoryOfThisPlace/>
</dict>
Adding the missing PowerShell script:
param(
[Parameter(Mandatory = $True)][string]$file1,
[Parameter(Mandatory = $True)][string]$file2,
[Parameter(Mandatory = $True)][string]$path
)
# using only abs paths .. just to be safe
$file1 = Join-Path $(Get-Location) $file1
$file2 = Join-Path $(Get-Location) $file2
$path = Join-Path $(Get-Location) $path
# awesome xsl stylesheet from Oliver Becker
# http://web.archive.org/web/20160502194427/http://www2.informatik.hu-berlin.de/~obecker/XSLT/merge/merge.xslt
$xsltfile = Join-Path $(Get-Location) "merge.xslt"
$XsltSettings = New-Object System.Xml.Xsl.XsltSettings
$XsltSettings.EnableDocumentFunction = 1
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$xslt.Load($xsltfile , $XsltSettings, $(New-Object System.Xml.XmlUrlResolver))
[System.Xml.Xsl.XsltArgumentList]$al = [System.Xml.Xsl.XsltArgumentList]::new()
$al.AddParam("with", "", $file2)
$al.AddParam("replace", "", "true")
[System.Xml.XmlWriter]$xmlwriter = [System.Xml.XmlWriter]::Create($path)
$xslt.Transform($file1, $al, $xmlwriter)
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!
java -jar saxon9he.jar .\FileA.xml .\merge.xslt with=FileB.xml replace=true
~ happy hacking!
Links:
- https://www.simple-talk.com/sysadmin/powershell/powershell-data-basics-xml/
- http://web.archive.org/web/20160502194427/http://www2.informatik.hu-berlin.de/~obecker/XSLT/merge/merge.xslt.html
Update: replaced $args in Code because of PowerShell 5.1 (thanks https://outofmemoryexception.wordpress.com/2016/08/05/powershell-5-1-14393/ )