提问人:am2 提问时间:11/17/2023 更新时间:11/17/2023 访问量:50
PowerShell JSON:如何找出,如果密钥存在=
PowerShell JSON: How to find out, if key exists=
问:
我使用 Powershell 5.1 我尝试使用(简单的)JSON文件作为脚本之间的信箱。 因此,在脚本的开头,我读取了文件,对其进行了修改,并将其存储在末尾。 例如,以下文件
{
"A": {
"April": "4",
"August": "8"
},
"J": {
"January": "1",
"July": "7"
}
}
为了更好地处理,我编写了一个 Read-Value 和一个 Write-Value 函数。在此示例中,Read-Value 显示了我的问题:该函数找不到参数“Name”。
$config_file_full = Join-Path -Path $PSScriptRoot -ChildPath 'months.json'
$config_json = Get-Content -Raw -Path $config_file_full | ConvertFrom-Json
function Read-Value {
param (
[string]$Subject,
[string]$Name
)
# $result = $null
if (-not ($config_json.PSObject.Properties.Name -contains $Subject)) {
Write-Host "READ NO MATCH for SUBJECT " $Subject
$result = $null
} elseif (-not ($config_json.PSObject.Properties.$Subject.Name -contains $Name)) {
Write-Host "READ NO MATCH FOR SUBJECT " $Subject "NAME " $Name
$result = $null
} else {
$result = $config_json.$Subject.$Name
Write-Host "READ NO MATCH FOR SUBJECT " $Subject "NAME " $Name "VALUE <" $result ">"
}
return $result
}
$a = Read-Value -Subject 'A' -Name 'April'
Write-Host "<" $a ">"
$config_json | ConvertTo-Json | Set-Content -Path $config_file_full
结果是
READ NO MATCH FOR SUBJECT A NAME April
< >
怎么了?elseif- 条件未按预期工作。 谢谢
答:
这里有几个问题。
- 此处的 PSObject.Properties 是实例的集合(由于@mklement0已更正),这些实例不允许你尝试使用点表示法对每个属性进行寻址。
PSPropertyInfo
- 即使您到达要检查的 Subject,该属性也包含该 Subject 的名称,而不是您正在检查的名称。
$Subject
Name
$Name
这应该效果更好:
function Read-Value {
param (
[string]$Subject,
[string]$Name
)
if (-not ($config_json.PSObject.Properties.Name -contains $Subject)) {
Write-Host "READ: No match for SUBJECT '$($Subject)'"
} elseif (-not ($config_json.PSObject.Properties[$Subject].Value.PSObject.Properties.Name -contains $Name)) {
Write-Host "READ: Matched Subject '$($Subject)' but not Name '$($Name)'"
} else {
Write-Host "READ: Matched Subject '$($Subject)' and Name '$($Name)': <$($config_json.$Subject.$Name)>"
$config_json.$Subject.$Name
}
}
James Ruskin 的回答将解决您的问题,但如果您用 2 个单独的块替换您的语句,然后在错误情况下使用“提前退出”,则可以更优雅地完成:if/elseif/else
if
return
function Read-Value {
param (
[string]$Subject,
[string]$Name
)
if (-not ($config_json.PSObject.Properties.Name -contains $Subject)) {
Write-Host "READ: No match for SUBJECT '$($Subject)'"
return
}
# if we've reached this point it must mean the subject exists, assign to a variable
$subjectObject = $config_json.PSObject.Properties[$Subject].Value
if (-not ($subjectObject.PSObject.Properties.Name -contains $Name)) {
Write-Host "READ: Matched Subject '$($Subject)' but not Name '$($Name)'"
return
}
Write-Host "READ: Matched Subject '$($Subject)' and Name '$($Name):<$($result)>"
$config_json.$Subject.$Name
}
为了补充现有的有用答案:
使用可能更简单但不完全等效的解决方案:
PowerShell 通常允许使用变量引用等表达式作为属性名称,例如
$config_json.$Subject
如果不需要区分属性是否存在以及属性是否存在但包含
$null
,则可以使用此功能来简化函数:function Read-Value { param ( [string]$Subject, [string]$Name ) Set-StrictMode -Version 1 if ($null -eq ($subject = $config_json.$Subject)) { Write-Host "READ NO MATCH for SUBJECT " $Subject } elseif ($null -eq ($result = $subject.$Name)) { Write-Host "READ NO MATCH FOR SUBJECT " $Subject "NAME " $Name } else { Write-Host "READ MATCH FOR SUBJECT " $Subject "NAME " $Name "result <" $result ">" $value # Output } }
并详细说明了
为什么$config_json。PSObject.Properties.$Subject
不起作用,不像 ,即使它尝试使用相同的技术:见下文。$config_json.PSObject.Properties[$Subject]
固有 psobject
属性的属性是任何对象的丰富反射源。.Properties
它包含 System.Management.Automation.PSPropertyInfo
实例的集合[1],每个实例都包含有关对象属性的信息,特别是 和 .
集合本身的类型为 System.Management.Automation.PSMemberInfoCollection<T>
[2].Name
.Value
由于 PowerShell 方便的成员访问枚举功能,你可以使用该集合的元素检索属性值;也就是说,此表达式等效于:$config_json.PSObject.Properties.Name
.Name
$config_json.PSObject.Properties | ForEach-Object { $_.Name }
因此,不起作用,因为集合中的 -派生实例没有名为 的属性,例如。$config_json.PSObject.Properties.$Subject
PSPropertyInfo
A
然而:
PSMemberInfoCollection<T>
通过其参数化.Item
属性,允许通过其属性的值检索 -派生元素,该属性可以通过索引表示法访问;例如:PSPropertyInfo
.Name
# OK -> [PSNoteProperty] instance describing the .Bar property. # Append .Value to extract the value only. ([pscustomobject] @{ Foo=1; Bar=2 }).psobject.Properties['Bar']
但是,由于它不是真正的字典,因此由于未实现
IDictionary
接口,因此不支持通常等效的点表示法。# !! $null - dot notation does NOT work here: looks for a .Bar # !! property on every [PSNoteProperty] element, via member-access enumeration. ([pscustomobject] @{ Foo=1; Bar=2 }).psobject.Properties.Bar # By contrast, with a true dictionary such as [hashtable], index notation and # dot notation are largely interchangeable: # OK -> 2 @{ Foo=1; Bar=2 }['Bar'] # OK -> 2 @{ Foo=1; Bar=2 }.Bar
[1] 从技术上讲,它们是从此抽象类派生的类型的实例,例如 System.Management.Automation.PSProperty,或者在本例中为 System.Management.Automation.PSNoteProperty
。
[2] 从技术上讲,它的类型为 [System.Management.Automation.PSMemberInfoIntegratingCollection<T>]
,这是一种派生自该类型的非公共类型。
评论
} elseif (-not ($config_json.PSObject.Properties[$Subject].Value.PSObject.Properties.Name -contains $Name)) {