之前寫過比對兩個 JSON 物件陣列差異的 .NET 小工具,用來對照多哪幾筆、少哪幾筆,哪幾筆的哪幾個欄位不同。這回場景類似,但要改用 PowerShell 實現,當成暖身練習。

先準備測試資料如下,故意讓 src.json 與 dst.json 二者有一筆新增、一筆缺少、一筆相同、一筆欄位值不同。

src.json

[
    { "Key": "Same", "Text": "Consistent", "Code": 0 },
    { "Key": "Diff", "Text": "Original", "Code": 0 },
    { "Key": "Miss", "Text": "Missing", "Code": 0 }
]

dst.json

[
    { "Key": "Same", "Text": "Consistent", "Code": 0 },
    { "Key": "Diff", "Text": "Modified", "Code": 1 },
    { "Key": "New", "Text": "New Data", "Code": 1 }
]

一陣子沒寫 PowerShell,這回復習了幾個小技巧:

  1. ConvertFrom-Json 轉換出來的物件陣列要用括號包起來或存入變數才能正常跑 ForEach-Object/Where-Object。參考:PowerShell ConvertFrom-Json 還原陣列無法 Where-Object 篩選
  2. JSON 換成的自訂物件(PSCustomObject)可用 Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name 列舉屬性名稱字串。
    PowerShell 自訂物件的屬性型別為 NoteProperty,例如以下範例:
  3. 要用字串變數當屬性名稱的寫法為 $custObject.$propName (不是 $custObject[$propName]):

程式範例如下:

param (
    [Parameter(Mandatory=$true)]
    [string]$srcPath,
    [Parameter(Mandatory=$true)]
    [string]$dstPath
)
$ErrorActionPreference = "Stop"
$src = @{}
# https://blog.darkthread.net/blog/ps-convertfrom-json-pipeline-issue/
# 使用 ( ) 包住 ConvertFrom-Json 結果以視為一般陣列處理
(Get-Content $srcPath | ConvertFrom-Json) | ForEach-Object { $src[$_.Key] = $_ }
$dst = @{}
(Get-Content $dstPath | ConvertFrom-Json) | ForEach-Object { $dst[$_.Key] = $_ }
# 找出 $dst 有但 $src 沒有的項目
$dst.Keys | Where-Object { !$src.ContainsKey($_) } | ForEach-Object { 
    Write-Host "多出: $($_)=$($dst[$_] | ConvertTo-Json)" 
}
# 找出 $src 有但 $dst 缺少的項目
$src.Keys | Where-Object { !$dst.ContainsKey($_) } | ForEach-Object { 
    Write-Host "缺少: $($_)=$($src[$_] | ConvertTo-Json)" 
}
# 找出 $src 與 $dst 有但值不同的項目
$src.Keys | Where-Object { $dst.ContainsKey($_) -and $src.ContainsKey($_)} | ForEach-Object { 
    # 比較兩物件的所有欄位找出差異 (假設二物件擁有相同欄位)
    $key = $_
    $srcObj = $src[$key]
    $dstObj = $dst[$key]
    $diff = @{}
    # Get-Member 取得物件所有 NoteProperty (屬性) 並取出 Name (屬性名稱)
    $src[$key] | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object { 
        $propName = $_
        # 可以用變數當屬性名稱取值
        if ($srcObj.$propName -ne $dstObj.$propName) {
            $diff[$propName] = "$($srcObj.$propName) => $($dstObj.$propName)" 
        }
    }
    if ($diff.Count -gt 0) {
        Write-Host "差異: $($_)=$($diff | ConvertTo-Json)" 
    }
}

筆記備用。

PowerShell example to compare two custom object array JSONs with PowerShell.


Comments

Be the first to post a comment

Post a comment