Powershell: Compare two XML files (Node List)

Often we come across situation to compare two reports. Report may be of any format and without knowing schema, it is difficult to process programmatically. One of the effective solution is to create XML reports and then write a powershell to compare the XMLs knowing the schema. Below is powershell script that basically reads data from two XML files and parses through the data for comparison. You have to specify the Node List object Name that you need to compare and works only for Node List with depth of 2 levels.

###################################################################################################################################################
# Configure the Variables before executing script.
# C:\Temp is location configured and XML reports should be named as <Computer1>_Report.xml and <Computer2>_Report.xml
###################################################################################################################################################
param (
$Computer1,
$Computer2,
$NodeListName
)
#Initializing Variables
cls
# Create header for HTML Report
$Head = ""
$Head +="BODY{background-color:#CCCCCC;font-family:Calibri,sans-serif; font-size: small;}"
$Head +="TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width: 98%;}"
$Head +="TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:#293956;color:white;padding: 5px; font-weight: bold;text-align:left;}"
$Head +="TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:#F0F0F0; padding: 2px;}"
$Head +="TD:nth-child(3) {width: 10%;}"
$Head +="TD:nth-child(2) {width: 45%;}"
$Head +="TD:nth-child(1) {width: 45%;}"
$Head +=""
$ComputerName = hostname
$ReportOutput = $null
$FilePath = "C:\Temp\" + $ComputerName + "_Comparison_Report.htm"
$a = "C:\Temp\" + $Computer1 + "_Report.xml"
$b = "C:\Temp\" + $Computer2 + "_Report.xml"
If (Test-Path $FilePath){ Remove-Item $FilePath -Force}
[XML]$Comp1 = Get-Content $a
[XML]$Comp2 = Get-Content $b
$C1 = $Comp1.SelectNodes("//$NodeListName")
$C2 = $Comp2.SelectNodes("//$NodeListName")
for ($i = 0; $i -lt $C2.ChildNodes.Count; $i++){
    # Either Computer's Node has Empty Value
    If (($C1.ChildNodes[$i].ChildNodes.Count -eq "") -or ($C1.ChildNodes[$i].ChildNodes.Count -eq $null) -or (($C2.ChildNodes[$i].ChildNodes.Count -eq "") -or ($C2.ChildNodes[$i].ChildNodes.Count -eq $null))){
        $NodeName = $C1.ChildNodes[$i].LocalName
        If (($C1.ChildNodes[$i].ChildNodes.Count -gt 0) -or ($C2.ChildNodes[$i].ChildNodes.Count -gt 0)) { $Hash = @{"Parameter" = "$NodeName"; "Computer1" = $C1.ChildNodes[$i].ChildNodes.innerXML ; "Computer2" = $C2.ChildNodes[$i].ChildNodes.innerXML ; "Result" = "Failure" } }
        Else {$Hash = @{"Parameter" = "$NodeName"; "Computer1" = "No Data" ; "Computer2" = "No Data" ; "Result" = "Success" }}
        $ReportOutput += New-Object psobject -Property $Hash | Select Computer1,Computer2,Result | ConvertTo-Html -Fragment -PreContent "$NodeName"
    }
    #Both Computer's Node has text value
    ElseIf (($C1.ChildNodes[$i]."#text") -and ($C2.ChildNodes[$i]."#text")){
    #Compare
    $NodeName = $C1.ChildNodes[$i]|select -ExpandProperty name
    $Value1 = $C1.ChildNodes[$i]."#text"
    $Value2 = $C2.ChildNodes[$i]."#text"
    If ($Value2 -eq $Value1) {$Hash = @{"Parameter" = "$NodeName"; "Computer1" = $Value1 ; "Computer2" = $Value2 ; "Result" = "Success" }}
    Else {$Hash = @{"Parameter" = "$NodeName"; "Computer1" = $Value1 ; "Computer2" = $Value2 ; "Result" = "Failure" }}
    $ReportOutput += New-Object psobject -Property $Hash | Select Computer1,Computer2,Result | ConvertTo-Html -Fragment -PreContent "$NodeName"
    }
    #Both Computer's Node has 1 Child Count
    ElseIf (($C1.ChildNodes[$i].ChildNodes.Count -eq $C2.ChildNodes[$i].ChildNodes.Count) -and ($C1.ChildNodes[$i].ChildNodes.Count -eq 1)){
        $Name = $C2.ChildNodes[$i].Name
        $NodeName = $C2.SelectNodes("//$Name/*")|select -ExpandProperty LocalName
        $Value1 = $C1.SelectNodes("//$NodeName")
        $Value2 = $C2.SelectNodes("//$NodeName")
        $Props = $C2.SelectNodes("//$NodeName/*")|select -Unique -ExpandProperty Localname
        $Status = "Success"
        foreach ($Prop in $props){
            $Results = Compare-Object -ReferenceObject $value1 -DifferenceObject $Value2 -Property $prop -IncludeEqual
            If (($Results.SideIndicator -Contains "")) { $Status = "Failure" }
        }
        $Hash = @{"Parameter" = "$NodeName"; "Computer1" = $Value1.InnerXml ; "Computer2" = $Value2.InnerXml ; "Result" = "$Status" }
        $ReportOutput += New-Object psobject -Property $Hash | Select Computer1,Computer2,Result | ConvertTo-Html -Fragment -PreContent "$NodeName"
    }
    #Both Computer has equal number of Child Count
    ElseIf (($C1.ChildNodes[$i].ChildNodes.Count -gt 1) -or ($C2.ChildNodes[$i].ChildNodes.Count -gt 1)){
        #Compare
        $Name1 = $C2.ChildNodes[$i].Name
        $Name2 = $C2.SelectNodes("//$Name1/*")|select -Unique -ExpandProperty LocalName
        $Values1 = $C1.SelectNodes("//$Name2")
        $Values2 = $C2.SelectNodes("//$Name2")
        $Props = $C2.SelectNodes("//$Name2/*")|select -Unique -ExpandProperty Localname
        $Results = Compare-Object -ReferenceObject $values1 -DifferenceObject $Values2 -Property $Props -IncludeEqual -passThru
        $Objects = @()
        foreach ($result in $results) {
            $var = $null
            for ($j=0;$j -lt $props.count; $j++){
                $prop = $props[$j]
                $Var += $Result.$prop + ":"
            }
            $var = $var.Trim(":")
            If ($result.SideIndicator -eq "") { $Hash = @{"Parameter" = $Name2; "Computer1" = "Mismatch/NotFound" ; "Computer2" = "$var" ; "Result" = "Failure" }}
            If ($result.SideIndicator -eq "==") { $Hash = @{"Parameter" = $Name2; "Computer1" = "$var" ; "Computer2" = "$var" ; "Result" = "Success" }}
            $obj = new-object psobject -Property $Hash
            $Objects += $obj
        }
        $ReportOutput += $Objects | Select Computer1,Computer2,Result | ConvertTo-Html -Fragment -PreContent "$Name2"
    }
}
ConvertTo-HTML -head $Head -body "$ReportOutput" | Out-File $FilePath

You can download sample Input XML for which the script works perfectly. I hope you can take this script and develop further to suit your needs.

Happy Coding!

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.