PowerShell ConvertFrom-Json 還原陣列無法 Where-Object 篩選
0 |
寫了一段 PowerShell 從 JSON 還原陣列,打算濾掉部分內容將另存成 JSON 檔,不料遇到 Where-Object 篩選無效的問題。
以下是重現問題的範例:
$json = @"
[
{ "Nam": "Jeffrey", "Score": 255 },
{ "Name": "darkthread", "Score": 9999 },
{ "Name": "Ironman", "Score": 32767 }
]
"@
$json | ConvertFrom-Json | Where-Object { $_.Score -gt 256 } | ConvertTo-Json
預期結果應該只看到兩筆,但三筆都在。而且,等等! JSON 顯示它不是陣列,而是一個具有 value 跟 Count 屬性的物件。
問題出在 ConvertFrom-Json 會將陣列包成單一物件傳給 Pipeline,故簡化成 '[1,2,3]' | ConvertFrom-Json | ConvertTo-Json
也能重現問題:
解法很簡單,用括號把 ConvertTo-Json 結果包起來,或是將 ConvertTo-Json 結果存入變數都會強迫展開成陣列:
有趣的是,有些 Cmdlet 接到 Pipeline 傳來包成單一物件的陣列會自動展開,例如 Fomrat-List,但 Where-Object 不會,所以會出現下面這種結果:
不管有沒有包括號,Format-List 都能列出陣列的三個數字,但套上 Where-Object 條件,有加括號展開陣列傳入才有篩選效果。這個現象蠻玄的,害我在處理時多迷惑一陣子。
如果對背後的原理有興趣,這則 stackoverflow 回答提供了完整解釋,摘要重點如下:
- ConvertFrom-Json 解析陣列 JSON 字串時未依慣例一個一個元素傳入 Pipeline,而是整個包成一個 System.Array 物件傳下去。
- PowerShell ETS(Extended Type System) 為 System.Array 加入了 Count 屬性,而 ConvertTo-Json 顯示時會把它包含進來。用
,(1,2,3) | ConvertTo-Json
可以觀察到類似結果:
- 用括號包住或設為變數都有強制展開陣列的效果,這是 Workaround 有效的原因。
- ETS 加入 .Count 的行為 PSv3 起已歸為過時,但 PSv5.1 仍存在,PowerSell Core (PS6+) 之後已取消。
- 依此原理,除了加括號存變數,用 Remove-TypeData System.Array 也能解決問題:
Explaining the unexpected behavior of ConvertFrom-Json when passing array results to the pipeline.
Comments
Be the first to post a comment