我们如何处理Powershell中的json空值



我创建了请求API并获得响应的powershell脚本,但很多时候我在JSON对象中获得没有双引号的空值,这在其他脚本中产生了错误。

如何在获得空值的地方加上双引号。

响应:

{ "Name" : { 
"Tag1" : "server1",
"Tag2" : null,
"Tag3" : "web"
}
}

在这里,我期待"Tag2":"null"(需要双引号,得到空响应)

上下文: 我正在从 azure vm 获取标记,如果该特定标记不可用,则 itz 按预期返回 null。

Ex.
$webvmdata = @{
Vmtype = $wenvm.Tags.vmtype
}
$webvmdata | ConvertTo-Json

我们如何才能使用Powershell轻松处理这个问题。

提前谢谢。

注意:

  • 一般来说,null是一个合法的 JSON 值,当 PowerShell 使用其ConvertFrom-Jsoncmdlet 将 JSON 解析为自定义对象时,该值变得$null,实际上,该 cmdlet 也内置于Invoke-RestMethod中。

如果您确实需要在 JSON 中将null值转换为"null"值(即字符串),最可靠但不快速的方法是:

  • ConvertFrom-Json将 JSON 解析为自定义对象...
  • 。然后遍历这些对象的内部结构以查找$null属性值并将它们替换为字符串'null'...
    • 有关封装此行为的通用帮助程序函数Edit-LeafProperty,请参阅底部部分。
  • 。并将修改后的对象转换回 JSON。
# Parse JSON into custom object(s).
$fromJson = ConvertFrom-Json @'
{
"Name": {
"Tag1": "server1",
"Tag2": null,
"Tag3": "web"
}
}
'@
# Transform the object(s) in place.
$fromJson | ForEach-Object {
# Helper script block that walks the object graph
$sb = {
foreach ($el in @($args[0])) {
if ($el -is [System.Collections.IEnumerable]) { # Nested array
foreach ($subEl in $el) { & $sb $subEl } # Recurse
}
elseif ($el -is [System.Management.Automation.PSCustomObject]) {
foreach ($prop in $el.psobject.Properties) { 
# If the property value is $null, replace it with string 'null'
if ($null -eq $prop.Value) { $prop.Value = 'null' }
elseif ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
& $sb $prop.Value  # Recurse
}
} 
}
}
}
# Call the helper script block with the input object.
& $sb $_
} 
# Convert the transformed object(s) back to JSON
ConvertTo-Json $fromJson

输出(注意"null"):

{
"Name": {
"Tag1": "server1",
"Tag2": "null",
"Tag3": "web"
}
}

元素数组注意事项:

如果输入 JSON 是一个恰好只包含一个元素的数组 ([ ... ]),则在 PowerShell(Core) 7+中,您需要向ConvertFrom-Json调用添加-NoEnumerate,以便将 JSON 解析为 PowerShell 数组 - 否则,你将只获取该元素本身,而不是包装在数组中。(这在Windows PowerShell中不是必需的)。[1]


使用广义辅助函数Edit-LeafProperty

如果在下面定义帮助程序函数(在运行以下代码之前),则代码将简化为以下内容:

@'
{
"Name": {
"Tag1": "server1",
"Tag2": null,
"Tag3": "web"
}
}
'@  | ConvertFrom-Json | 
Edit-LeafProperty -PassThru { if ($null -eq $_.Value) { $_.Value = 'null' } } |
ConvertTo-Json

注意:

  • 每个叶属性(一个本身不包含另一个嵌套[pscustomobject]的对象属性)被传递给绑定到$_的指定脚本块({ ... }),在那里可以根据需要检查和更新其.Value(如果需要,.Name)属性。

  • -PassThru传递每个修改的对象(修改后输出),以便结果可以直接通过管道传输到ConvertTo-Json

    • 通常的警告是,输出的潜在意外截断适用:根据需要对深度超过 2 级的对象图使用
      -Depth- 请参阅这篇文章
  • PowerShell(核心)中重新保留阵列的警告与以前一样适用,但有一个额外的转折:

    • 使用ConvertFromJson -NoEnumerate来保留(可能嵌套的)单元素数组本身。
    • 由于使用了中间流式处理命令 -Edit-LeafProperty- 如果需要保证输出 JSON 是一个数组,即使只包含一个元素,也要使用ConvertTo-Json -AsArray

请注意,该函数设计为仅处理[pscustomobject]([System.Management.Automation.PSCustomObject])图,例如由ConvertFrom-JsonConvertFrom-Csv返回的图。

Edit-LeafProperty源代码

function Edit-LeafProperty {
[CmdletBinding(PositionalBinding = $false)]
param(
[Parameter(Mandatory, Position = 0)]
[scriptblock] $ScriptBlock,
[Parameter(Mandatory, ValueFromPipeline)]
[AllowNull()]
$InputObject,
[switch] $PassThru
)
begin {
# Helper script block that walks the object graph
$sb = {
foreach ($el in @($args[0])) {
if ($el -is [System.Collections.IEnumerable]) { # Nested array
foreach ($subEl in $el) { & $sb $subEl } # Recurse
}
elseif ($el -is [System.Management.Automation.PSCustomObject]) {
foreach ($prop in $el.psobject.Properties) { 
if ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
& $sb $prop.Value # Recurse
}
else {
# Invoke the leaf-property processing script block with
# the property at hand bound to $_
ForEach-Object $ScriptBlock -InputObject $prop
}
} 
}
}
}
}
process {
& $sb $InputObject # Walk the input object at hand.
if ($PassThru) { $InputObject } # Pass it through, if requested.
}
}

[1] 请参阅此答案,了解促使这种行为变化的原因。