Powershell - 递归查找并更新命名对象 属性 的所有实例

Powershell - Find and update all instances of named object property recursively

问题

我需要使用 powershell 更新大型导出的 JSON 文件中名为 sourceList 的 属性 的所有实例,但是 JSON 的结构不是与每次导出一致,属性 发生在不同的深度:

JSON

的示例结构
{
    "rows": [
        {
            "controls": [
                {
                    "properties": {
                        "sourceList": "I NEED TO CHANGE THIS"
                    }
                }
            ]
        },
        {
            "controls": [
                {
                    "properties": {
                        "rows": [
                            {
                                "controls": [
                                    {
                                        "properties": {
                                            "sourceList": "ALSO THIS, BUT IT'S MUCH DEEPER"
                                        }
                                    }
                                ]
                            }
                        ]
                    }
                }
            ]
        }
    ]
}

JSON 文件来自表单构建工具,因此不同的表单设计(例如将字段放在组容器中)可能会导致深度发生巨大变化。

当前代码

$formObj = Get-Content -Raw -Path $form | ConvertFrom-Json
# Find and update all instances of sourceList
$formObj | ConvertTo-Json -Depth 100 | Set-Content $form

与 JSON 的实际转换工作正常,我已经成功更改了一些具有可靠结构的其他属性,我的问题是无法找到并更新命名的 属性知道它会发生在什么深度。

为此您需要创建一个递归函数
为了使事情更简单,我建议您使用 ConvertFrom-Json -AsHashTable 参数,它将 return [HashTable] 个对象而不是 [PSCustomObject] 个对象

Function Set-Recurse ($Object, $Key, $Value) {
    if ($Object -is [HashTable]) {
        if ($Object.ContainsKey($Key)) { $Object[$Key] = $Value }
        $Object.get_Values() | ForEach-Object { Set-Recurse $_ $Key $Value}
    }
    elseif ($Object -is [Array]) {
        $Object | ForEach-Object { Set-Recurse $_ $Key $Value}
    }
}

用法:

$formObj = ConvertFrom-Json -AsHashTable '{
    "rows": [
        {
            "controls": [
                 ...
    ]
}'

Set-Recurse $formObj sourceList 'New Value'

$formObj | ConvertTo-Json -Depth 100

为什么不这样做...

Select-String -Path '.\FormTool.txt' -Pattern 'sourceList' -Context 0
# Results
<#
FormTool.txt:7:                        "sourceList": "I NEED TO CHANGE THIS"
FormTool.txt:21:                                            "sourceList": "ALSO THIS, BUT IT'S MUCH DEEPER"
#>

然后在行号或字符串匹配处修改

(Select-String -Path '.\FormTool.txt' -Pattern 'sourceList' -Context 0) -replace '\:\s".*', ': "Repalced with this stuff"'
# Results
<#
D:\Scripts\FormTool.txt:7:                        "sourceList": "Repalced with this stuff"
D:\Scripts\FormTool.txt:21:                                            "sourceList": "Repalced with this stuff"
#>