[英]Powershell: How to dynamically update a nested JSON array?
我想要做的是獲取任何格式良好的 JSON 文件/對象並在其中搜索路徑。 如果找不到路徑,請繼續。 如果找到,則更新該值。 更新后,將更新后的 JSON 保存到原始文件中。
問題在於,格式良好的 JSON 結構提前未知。 我可能正在磁盤上搜索數百個 .json 文件,因此可以忽略與我的任何搜索詞都不匹配的文件。
我正在努力思考如何解決這個問題。 大多數示例都沒有包含鍵值之一的數組的 JSON 對象,或者當涉及數組時,它們不會動態訪問屬性。
此鏈接: Powershell:如何更新/替換 Json 和 XML 對象中的數據和值顯示了(某種)“真實”的 JSON 結構,但接受的答案依賴於了解 JSON 結構是什么(OP 沒有要求幫助動態路徑)。
這個鏈接: Set Value of Nested Object Property by Name in PowerShell有一些非常接近的東西,盡管當數組混合時,它在設置時無法正常工作。
下面是一些用於解決此問題的示例 JSON,但同樣,在腳本運行之前,結構是未知的。 我正在遍歷磁盤上的文件列表,並為每個文件執行。
$JSON = ConvertFrom-Json '{
"key1":"key 1 value",
"options":{
"outDir":"./app-dir",
"lib":[
"someLibrary",
"anotherLibrary"
],
"someObjects":[
{
"first":"I am first"
},
{
"second":"I am second"
}
]
}
}'
搜索此 json 的字符串可能如下所示:
$SearchString = 'options.someObjects.first'
或者,一些不存在的東西,比如:
$SearchString = 'options.someObjects.foo'
使用第二篇文章中的遞歸函數 GetValue 可以很好地獲取(並且比我正在做的更優雅):
function GetValue($object, $key)
{
$p1,$p2 = $key.Split(".")
if($p2) { return GetValue -object $object.$p1 -key $p2 }
else { return $object.$p1 }
}
但是,函數 SetValue 不適用於數組。 它返回一個錯誤,指出“在這個對象上找不到屬性‘first’。”
function SetValue($object, $key, $Value)
{
$p1,$p2 = $key.Split(".")
if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
else { $object.$p1 = $Value }
}
我知道這是因為 $JSON.options.someObjects 是一個數組,因此要使用“first”鍵訪問對象,路徑將是:
$JSON.options.someObjects[0].first
這就是我遇到的問題。 一旦到達需要迭代的路徑的一部分,我如何動態迭代所有對象? 路徑的那部分可以在任何地方,或者向下更多層,等等......
奇怪的是,powershell 允許您在獲取值時動態迭代數組,但在嘗試設置它時不允許。
這是一個完整的示例,它演示了整個問題:
#Create JSON:
$JSON = ConvertFrom-Json '{
"key1":"key 1 value",
"options":{
"outDir":"./app-dir",
"lib":[
"someLibrary",
"anotherLibrary"
],
"someObjects":[
{
"first":"I am first"
},
{
"second":"I am second"
}
]
}
}'
$SearchPath = 'options.someObjects.first'
$NewValue = 'I am a new value'
function GetValue($object, $key)
{
$p1,$p2 = $key.Split(".")
if($p2) { GetValue -object $object.$p1 -key $p2 }
else { return $object.$p1 }
}
function SetValue($object, $key, $Value)
{
$p1,$p2 = $key.Split(".")
if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
else { return $object.$p1 = $Value }
}
GetValue -object $JSON -key $SearchPath
SetValue -object $JSON -key $SearchPath -Value $NewValue
我一直在搜索各種不同的術語,試圖為這個問題找到一個好的解決方案,但到目前為止,沒有運氣。 我很確定我不是第一個想做這種事情的人,如果我在某處錯過了答案,我深表歉意。
您的SetValue
腳本有兩個問題:
[Object]
) 與對象數組 ( [Object[]]
)你不能返回像return $object.$p1 = $Value
這樣的賦值。 賦值本身不返回任何內容,將導致將$Null
返回給調用者。
此外,如果您為每個遞歸調用返回$Object
,您將需要由每個父調用者將其設為 void ( $Null = SetValue -object...
),以便它僅由頂級調用者返回。 但請記住,您實際上是在原始 ( $JSON
) 對象中戳$NewValue
!。 如果您不希望那樣,您將需要找出頂級調用者,並且只在遞歸調用之前復制頂級的$Object
。
您不僅要處理包含單個對象的屬性,而且每個屬性都可能包含一個集合對象。 事實上,葉子屬性SomeObject
就是一個例子。 這意味着集合中的每個對象都有自己獨特的一組屬性(可以與兄弟對象具有相同的屬性名稱):
$JSON = ConvertFrom-Json '{
"key1":"key 1 value",
"options":{
"outDir":"./app-dir",
"lib":[
"someLibrary",
"anotherLibrary"
],
"someObjects":[
{
"first":"I am first"
},
{
"first":"I am first too"
},
{
"second":"I am second"
}
]
}
}'
請注意,您實際上可能會在 Json 對象的每個級別遇到對象集合。
從 PSv3 開始,你有一個名為Member Enumeration的功能,它可以讓你列出這些屬性,比如: ([Object[]]$SomeObject).First
但你不能像這樣設置(所有)相關屬性: ([Object[]]$SomeObject).First = $Value
。 (這就是為什么您的SetValue
函數不起作用而您的GetValue
函數起作用的原因。請注意,它實際上為上述“ I am first too
”Json 示例返回了兩個項目)。
換句話說,您需要遍歷每個級別上的所有對象集合以設置相關屬性:
function SetValue($object, $key, $Value)
{
$p1,$p2 = $key.Split(".",2)
if($p2) { $object | ?{$Null -ne $_.$p1} | %{SetValue -object $_.$p1 -key $p2 -Value $Value} }
else { $object | ?{$Null -ne $_.$p1} | %{$_.$p1 = $Value} }
}
SetValue -object $JSON -key $SearchPath -Value $NewValue
$Json | ConvertTo-Json -Depth 5
{
"key1": "key 1 value",
"options": {
"outDir": "./app-dir",
"lib": [
"someLibrary",
"anotherLibrary"
],
"someObjects": [
{
"first": "I am a new value"
},
{
"second": "I am second"
}
]
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.