PowerShell中ExpandProperty为何修改源对象?如何规避?
PowerShell展平嵌套对象数组时避免修改源对象的方法
当处理包含嵌套对象数组的对象数组时,使用Select-Object -ExpandProperty看似能实现展平需求,但会意外修改源对象中的嵌套元素——比如执行$obj | Select-Object -Property Name -ExpandProperty Books后,源对象里的每个Book对象会被添加父对象的Name属性,再次执行该命令会报错:
Select-Object : The property cannot be processed because the property "Name" already exists.
为什么会修改源对象?
ConvertFrom-Json生成的是动态的PSCustomObject实例,这类对象允许在运行时添加/修改属性。而Select-Object -ExpandProperty为了性能优化,没有为每个嵌套对象创建新实例,而是直接在原嵌套对象上附加指定的父属性(这里的Name),导致源对象被永久修改。
解决方案:不修改源对象实现展平
以下几种方法可以在不改动源对象的前提下,生成独立的展平结果:
方法1:手动构造新对象(最直观)
通过ForEach-Object遍历每个父对象,再遍历其嵌套的Books数组,为每个Book创建全新的PSCustomObject,同时附加父对象的属性:
$json = '[{"Name":"James","Age":42,"Books":[{"Title":"Consider Phlebas","Author":"Iain M. Banks"},{"Title":"The Player of Games","Author":"Iain M. Banks"}]},{"Name":"Susan","Age":36, "Books":[{"Title":"The Three Body Problem","Author":"Cixin Liu"},{"Title":"The Dark Forest","Author":"Cixin Liu"}]}]' $obj = $json | ConvertFrom-Json # 生成独立的展平结果,不修改源对象 $flattened = $obj | ForEach-Object { $parentName = $_.Name $_.Books | ForEach-Object { [PSCustomObject]@{ Name = $parentName Title = $_.Title Author = $_.Author } } } $flattened | Format-Table
方法2:使用Select-Object的计算属性(更简洁)
通过计算属性强制创建新对象,避免直接修改原嵌套元素:
$flattened = $obj | ForEach-Object { $parent = $_ $parent.Books | Select-Object @{Name='Name'; Expression={$parent.Name}}, Title, Author } $flattened | Format-Table
方法3:深拷贝源对象(适合复杂场景)
如果需要多次对源对象执行操作,可以先深拷贝一份,避免影响原数据:
# 通过ConvertTo-Json+ConvertFrom-Json实现深拷贝 $objCopy = $obj | ConvertTo-Json -Depth 10 | ConvertFrom-Json # 对拷贝后的对象执行操作,不会影响原$obj $objCopy | Select-Object -Property Name -ExpandProperty Books
验证
执行上述任意方法后,检查源对象的Books属性:
$obj[0].Books | Get-Member -MemberType NoteProperty
会发现源对象的Book元素并没有新增Name属性,命令可以重复执行而不报错。
内容的提问来源于stack exchange,提问作者Matt Tebbs
相关产品推荐
相关产品推荐

