簡體   English   中英

Powershell:如何動態更新嵌套的 JSON 數組?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM