You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell递归合并JSON对象时数组合并异常排查

PowerShell 递归合并JSON数组缺陷修复方案

现有自定义Merge-Json方法的普通对象属性合并逻辑可正常工作,能正确实现新增属性、覆盖标量属性值的效果;数组处理逻辑存在缺失,遇到数组类型属性时直接整体替换,会触发PowerShell对象类型转换的隐式行为,导致原数组结构被转为普通对象、数组元素内未覆盖的属性丢失,无法实现按索引合并数组元素、保留原有属性的预期。

核心修复方式是在递归判断逻辑中新增数组类型的专属处理分支,对两个数组按索引对齐后逐个递归合并对应元素,长度不一致时自动保留较长侧多出来的元素,避免直接整体替换。修复后的完整实现如下:

function AddPropertyRecurse {
    param(
        [PSCustomObject]$Target,
        [PSCustomObject]$Source
    )
    foreach ($prop in $Source.PSObject.Properties) {
        # 目标不存在该属性,直接添加
        if ($Target.PSObject.Properties.Name -notcontains $prop.Name) {
            $Target | Add-Member -NotePropertyName $prop.Name -NotePropertyValue $prop.Value
            continue
        }
        $targetProp = $Target.$($prop.Name)
        $sourceProp = $prop.Value

        # 两边都是PSCustomObject,递归合并
        if ($targetProp -is [PSCustomObject] -and $sourceProp -is [PSCustomObject]) {
            AddPropertyRecurse -Target $targetProp -Source $sourceProp
            continue
        }

        # 数组类型专属处理分支:按索引递归合并对应元素
        if ($targetProp -is [array] -and $sourceProp -is [array]) {
            $mergedArray = @()
            $maxLen = [Math]::Max($targetProp.Count, $sourceProp.Count)
            for ($i = 0; $i -lt $maxLen; $i++) {
                # 索引超过原数组长度,直接取待合并数组元素
                if ($i -ge $targetProp.Count) {
                    $mergedArray += $sourceProp[$i]
                    continue
                }
                # 索引超过待合并数组长度,保留原数组元素
                if ($i -ge $sourceProp.Count) {
                    $mergedArray += $targetProp[$i]
                    continue
                }
                # 对应位置元素都是对象,递归合并属性
                if ($targetProp[$i] -is [PSCustomObject] -and $sourceProp[$i] -is [PSCustomObject]) {
                    $tempObj = [PSCustomObject]@{}
                    # 先拷贝原元素所有属性,避免修改原对象
                    foreach ($p in $targetProp[$i].PSObject.Properties) {
                        $tempObj | Add-Member -NotePropertyName $p.Name -NotePropertyValue $p.Value
                    }
                    AddPropertyRecurse -Target $tempObj -Source $sourceProp[$i]
                    $mergedArray += $tempObj
                }
                else {
                    # 标量值直接用待合并值覆盖
                    $mergedArray += $sourceProp[$i]
                }
            }
            # 替换原数组为合并后结果
            $Target.$($prop.Name) = $mergedArray
            continue
        }

        # 其余类型(标量、类型不匹配场景)直接用待合并值覆盖
        $Target.$($prop.Name) = $sourceProp
    }
}

function Join-Objects {
    param(
        [PSCustomObject]$Target,
        [PSCustomObject]$Source
    )
    # 先拷贝原目标对象属性,避免修改原始输入对象
    $result = [PSCustomObject]@{}
    foreach ($prop in $Target.PSObject.Properties) {
        $result | Add-Member -NotePropertyName $prop.Name -NotePropertyValue $prop.Value
    }
    AddPropertyRecurse -Target $result -Source $Source
    return $result
}

function Merge-Json {
    param(
        [Parameter(Mandatory=$true)]
        [string]$TargetJsonPath,
        [Parameter(Mandatory=$true)]
        [string]$SourceJsonPath
    )
    $target = Get-Content $TargetJsonPath -Raw | ConvertFrom-Json
    $source = Get-Content $SourceJsonPath -Raw | ConvertFrom-Json
    $merged = Join-Objects -Target $target -Source $source
    # 指定足够大的序列化深度,避免默认深度2导致的深层对象转字符串问题
    return $merged | ConvertTo-Json -Depth 100
}

测试验证样例

下面是两组测试用JSON文件内容,可直接用于验证合并效果:

基础配置文件 target.json

{
  "AppConfig": {
    "JobServer3Camadas": true,
    "Version": "1.0.0"
  },
  "DbConfig": {
    "AppServer": [
      {
        "Name": "App1",
        "DbType": "SqlServer",
        "ConnectionString": "Server=old;Database=App1"
      },
      {
        "Name": "App2",
        "DbType": "PostgreSql",
        "ConnectionString": "Server=old;Database=App2"
      }
    ],
    "JobServer": [
      {
        "Name": "Job1",
        "DbType": "SqlServer",
        "MaxConcurrency": 5
      }
    ]
  }
}

待合并覆盖配置文件 source.json

{
  "AppConfig": {
    "JobServer3Camadas": false,
    "Teste": "NewProperty"
  },
  "DbConfig": {
    "AppServer": [
      {
        "ConnectionString": "Server=new;Database=App1"
      },
      {
        "ConnectionString": "Server=new;Database=App2"
      }
    ],
    "JobServer": [
      {
        "MaxConcurrency": 10
      }
    ]
  }
}

合并效果说明

调用命令:

Merge-Json -TargetJsonPath .\target.json -SourceJsonPath .\source.json

输出结果完全符合预期:

  • AppConfig节点:原有Version属性保留,JobServer3Camadas值被正确覆盖为false,新增Teste属性
  • DbConfig下的AppServer、JobServer数组:
    • 数组结构完整保留,不会被转换为普通对象
    • 按索引对齐递归合并数组内的对象,原有未被覆盖的属性(如Name、DbType)全部保留
    • 待合并配置中指定的属性(如新的ConnectionString、调整后的MaxConcurrency值)正确覆盖原有值
    • 若两个待合并数组长度不一致,较长侧多出来的元素会直接追加到合并结果中,不会丢失

注意:当前数组合并逻辑为按索引位置对齐匹配,如果业务场景需要按数组元素的唯一键(如Name字段)匹配合并而非按索引,仅需修改数组分支内的元素匹配规则即可,整体递归框架无需调整。

内容的提问来源于stack exchange,提问作者Leandro Campos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 23:42:19