递归移除PSObject转JSON空列表的PowerShell函数优化求助
处理PSObject转JSON时移除空列表的递归函数优化
问题描述
我构建了一个PSObject,转换为JSON后包含大量空列表,需要移除这些空列表以作为API更新数据的HTTP请求体。尝试编写递归处理函数,但遇到两个问题:仅对象的最后一项接近预期目标,且部分属性在递归调用后未被正确移除。现有函数会解析原PSObject属性,针对add、remove节点递归调用,手动排查并通过Select-Object移除空属性,但效果不佳。
原始JSON示例
{"add": {"windows": {"files": [], "directories": []}, "Applications": [{"processfile": {"sha2": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "name": "Aurelien test.exe"}}], "Certificates": [], "webdomains": [], "ips_hosts": [], "Extensions": {"names": [], "scheduled": false, "features": []}, "linux": {"files": [], "directories": []}, "mac": {"files": [], "directories": []}}, "remove": {"Applications": [], "Certificates": [], "webdomains": [], "ips_hosts": [], "Extensions": {"names": [], "scheduled": false, "features": []}, "windows": {"files": [], "directories": []}, "linux": {"files": [], "directories": []}, "mac": {"files": [], "directories": []}}}
预期JSON结果
{"add": {"Applications": [{"processfile": {"sha2": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "name": "Aurelien test.exe"}}]}}
现有函数代码
function Optimize-SepCloudAllowListPolicyObject { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSObject] $obj ) process { $AllProperties = $obj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name foreach ($property in $AllProperties) { switch ($property) { { $_ -in @("add", "remove") } { Optimize-SepCloudAllowListPolicyObject $obj.$property } "applications" { if ($obj.$property.processfile.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "webdomains" { if ($obj.$property.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "certificates" { if ($obj.$property.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "ips_hosts" { if ($obj.$property.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "extensions" { if ($obj.$property.names.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "linux" { if ($obj.$property.linux.files.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "files" } if ($obj.$property.linux.directories.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } } "windows" { if ($obj.$property.windows.files.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "files" } if ($obj.$property.windows.directories.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } } "mac" { if ($obj.$property.mac.files.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "files" } if ($obj.$property.mac.directories.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } } Default {} } } return $obj } }
优化指引与改进后的函数
现有函数的核心问题
- 递归调用未赋值:处理
add/remove节点时,仅调用递归函数但未将返回的处理结果赋值回原属性,导致递归处理的修改未被保留。 - 大小写不匹配:原始对象属性为
Applications(首字母大写),但函数中判断的是小写applications,对应逻辑完全未触发。 - 嵌套属性访问错误:例如
$obj.$property.linux.files,$obj.$property已经是linux对象,无需再嵌套linux访问,导致判断逻辑失效。 - 未处理空对象:当子属性被全部移除后(如
windows的files和directories都为空被移除),windows会变成空对象,但现有函数未进一步移除这类空对象;同理remove节点所有子属性为空时,未被整体移除。 - 硬编码属性判断:每个属性单独写判断逻辑,扩展性差且容易遗漏。
改进后的函数
function Optimize-SepCloudAllowListPolicyObject { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSObject]$obj ) process { # 创建新对象存储处理后的结果,避免修改原对象导致的遍历问题 $processedObj = [PSCustomObject]@{} $allProperties = $obj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name foreach ($property in $allProperties) { $value = $obj.$property # 递归处理嵌套对象 if ($value -is [PSObject]) { $processedValue = Optimize-SepCloudAllowListPolicyObject $value # 若处理后的值为空(无属性),则跳过添加该属性 if (-not [string]::IsNullOrEmpty($processedValue | Get-Member -MemberType NoteProperty -ErrorAction SilentlyContinue)) { $processedObj | Add-Member -MemberType NoteProperty -Name $property -Value $processedValue } } # 处理数组:仅保留非空数组 elseif ($value -is [array]) { if ($value.Count -gt 0) { $processedObj | Add-Member -MemberType NoteProperty -Name $property -Value $value } } # 处理其他类型(如布尔值):直接保留,如需移除特定值可在此添加判断 else { $processedObj | Add-Member -MemberType NoteProperty -Name $property -Value $value } } # 返回处理后的对象,若为空则返回$null(上层会自动移除该属性) if ($processedObj | Get-Member -MemberType NoteProperty -ErrorAction SilentlyContinue) { return $processedObj } else { return $null } } }
关键优化点说明
- 递归赋值与结果保留:递归处理嵌套对象后,将返回的有效结果添加到新对象中,确保修改被保留。
- 通用空值过滤:统一判断数组是否为空、对象是否无属性,自动跳过空内容,无需硬编码每个属性。
- 避免原对象修改问题:创建新对象存储处理结果,避免遍历原对象时修改属性导致的遍历异常。
- 自动移除空父节点:如果处理后的子对象为空,则不添加到父对象中,最终自动移除空的
remove节点、空的windows/linux等节点。 - 灵活扩展:如需移除特定值(如
false),可在非数组/对象的处理分支中添加判断逻辑。
测试验证
将原始JSON转换为PSObject后调用优化函数,再转JSON即可得到预期结果:
# 原始JSON转PSObject $originalObj = '{"add": {"windows": {"files": [], "directories": []}, "Applications": [{"processfile": {"sha2": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "name": "Aurelien test.exe"}}], "Certificates": [], "webdomains": [], "ips_hosts": [], "Extensions": {"names": [], "scheduled": false, "features": []}, "linux": {"files": [], "directories": []}, "mac": {"files": [], "directories": []}}, "remove": {"Applications": [], "Certificates": [], "webdomains": [], "ips_hosts": [], "Extensions": {"names": [], "scheduled": false, "features": []}, "windows": {"files": [], "directories": []}, "linux": {"files": [], "directories": []}, "mac": {"files": [], "directories": []}}}' | ConvertFrom-Json # 处理对象 $optimizedObj = Optimize-SepCloudAllowListPolicyObject $originalObj # 转换为JSON $optimizedObj | ConvertTo-Json -Depth 10
输出结果与预期完全一致。
内容的提问来源于stack exchange,提问作者Douda
相关产品推荐
相关产品推荐

