给定以下的
$object
,其中有3个属性,其中一个是
AdditionalProperties
,它是一个
Dictionary<TKey,TValue>
:
$dict = [Collections.Generic.Dictionary[object, object]]::new()
$dict.Add('displayName', 'placeholder')
$dict.Add('mail', 'placeholder')
$dict.Add('userPrincipalName', 'placeholder')
$object = [pscustomobject]@{
DeletedDateTime = 'placeholder'
Id = 'placeholder'
AdditionalProperties = $dict
}
假设你对这个对象中的
Id
、
displayName
和
mail
感兴趣,你可以使用
Select-Object
和
计算属性:
$object | Select-Object @(
'Id'
@{
Name = 'displayName'
Expression = { $_.additionalProperties['displayName'] }
}
@{
Name = 'mail'
Expression = { $_.additionalProperties['mail'] }
}
)
然而,当你需要从对象中选择更多的属性值时,使用循环来取出
PSCustomObject
的属性值会变得混乱无章。
$object | ForEach-Object {
[pscustomobject]@{
Id = $_.Id
displayName = $_.additionalProperties['displayName']
mail = $_.additionalProperties['mail']
}
}
这两种方法都会输出相同的"扁平化"对象,可以轻松转换为Csv格式:
Id displayName mail
-- ----------- ----
placeholder placeholder placeholder
作为Csv文件返回
"Id","displayName","mail"
"placeholder","placeholder","placeholder"
在这个意义上,你可以使用上述技术之一构建一个对象数组,例如:
Get-MgGroupMember -GroupId $GroupID | ForEach-Object {
[pscustomobject]@{
Id = $_.id
displayName = $_.additionalproperties['displayName']
mail = $_.additionalproperties['mail']
userPrincipalName = $_.additionalproperties['userPrincipalName']
}
}
如果您正在寻找一种编程方式来展开对象,您可以从以下示例开始。但是需要注意的是,此方法仅适用于属性嵌套只有一层的对象,换句话说,它无法处理递归:
$newObject = [ordered]@{}
foreach($property in $object.PSObject.Properties) {
if($property.Value -is [Collections.IDictionary]) {
foreach($addproperty in $property.Value.GetEnumerator()) {
$newObject[$addproperty.Key] = $addproperty.Value
}
continue
}
$newObject[$property.Name] = $property.Value
}
[pscustomobject] $newObject
这将生成一个扁平化的对象,就像这样,也可以轻松转换为Csv:
DeletedDateTime : placeholder
Id : placeholder
displayName : placeholder
mail : placeholder
userPrincipalName : placeholder
值得注意的是,上面的示例未处理可能的键冲突,如果有两个或更多具有相同名称的属性,则一个将覆盖其他属性。
奖励功能应该能够与从Graph, AzureAD 和Az模块返回的对象一起使用。该函数可以用于展平它们的Dictionary`2
属性。如果属性值实现了IDictionary
,那么它只会查看一层深度,因此不要期望它可以展平任何对象。对于给定的示例应该可以正常工作。
function Select-GraphObject {
[CmdletBinding(PositionalBinding = $false)]
param(
[Parameter(Mandatory, ValueFromPipeline, DontShow)]
[object] $InputObject,
[Parameter(Position = 0)]
[SupportsWildcards()]
[string[]] $Properties = '*'
)
begin {
$firstObject = $true
$toSelect = [Collections.Generic.List[object]]::new()
}
process {
if($firstObject) {
foreach($property in $InputObject.PSObject.Properties) {
foreach($item in $Properties) {
if($property.Value -is [Collections.IDictionary]) {
foreach($key in $property.Value.PSBase.Keys) {
if($key -like $item -and $key -notin $toSelect.Name) {
$toSelect.Add(@{
$key = { $_.($property.Name)[$key] }
})
}
}
continue
}
if($property.Name -like $item -and $property.Name -notin $toSelect) {
$toSelect.Add($property.Name)
}
}
}
$firstObject = $false
}
$out = [ordered]@{}
foreach($item in $toSelect) {
if($item -isnot [hashtable]) {
$out[$item] = $InputObject.$item
continue
}
$enum = $item.GetEnumerator()
if($enum.MoveNext()) {
$out[$enum.Current.Key] = $InputObject | & $enum.Current.Value
}
}
[pscustomobject] $out
}
}
使用上面示例中的
$object
副本,如果使用
-Properties
的默认值,示例对象将被"展平":
PS /> $object, $object, $object | Select-GraphObject
DeletedDateTime Id displayName mail userPrincipalName
--------------- -- ----------- ---- -----------------
placeholder placeholder placeholder placeholder placeholder
placeholder placeholder placeholder placeholder placeholder
placeholder placeholder placeholder placeholder placeholder
或者我们可以过滤特定的属性,甚至是从AdditionalProperties
属性中获取键:
PS /> $object, $object, $object | Select-GraphObject Id, disp*, user*
Id displayName userPrincipalName
-- ----------- -----------------
placeholder placeholder placeholder
placeholder placeholder placeholder
placeholder placeholder placeholder
AdditionalProperties
是一个IDictionary
,除非cmdlet内置了一些东西,否则您需要循环遍历键/值对并创建一个对象。您现在正在将字典转换为字符串,这将不容易在之后进行操作。 - Santiago Squarzon