5

PowerShell ConvertFrom-Json 還原陣列無法 Where-Object 篩選

 3 years ago
source link: https://blog.darkthread.net/blog/ps-convertfrom-json-pipeline-issue/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
PowerShell ConvertFrom-Json 還原陣列無法 Where-Object 篩選-黑暗執行緒

寫了一段 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 回答提供了完整解釋,摘要重點如下:

  1. ConvertFrom-Json 解析陣列 JSON 字串時未依慣例一個一個元素傳入 Pipeline,而是整個包成一個 System.Array 物件傳下去。
  2. PowerShell ETS(Extended Type System) 為 System.Array 加入了 Count 屬性,而 ConvertTo-Json 顯示時會把它包含進來。用 ,(1,2,3) | ConvertTo-Json可以觀察到類似結果:
  3. 用括號包住或設為變數都有強制展開陣列的效果,這是 Workaround 有效的原因。
  4. ETS 加入 .Count 的行為 PSv3 起已歸為過時,但 PSv5.1 仍存在,PowerSell Core (PS6+) 之後已取消。
  5. 依此原理,除了加括號存變數,用 Remove-TypeData System.Array 也能解決問題:
and has 0 comments

Comments

Be the first to post a comment

Post a comment

Comment
Name Captcha 11 + 11 =

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK