繁体   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