【问题标题】:Dynamically Build JSON - PowerShell动态构建 JSON - PowerShell
【发布时间】:2021-11-29 09:57:16
【问题描述】:

我正在编写一个 PowerShell 函数来构建将传递给 API 的 JSON。我正在使用 PowerShell 5。

API 需要某种格式,下面是一个示例。

{
    “task”: {
        “status”: {
            “name”: “Resolved”
                },
        “owner”: {
            “name”: “User”
        },
        “comment”: “Test”
    }
}

JSON 的结构存储在嵌套的哈希表中,然后通过管道传输到 ConvertTo-Json,我通过参数传递数据值。

$baseTask = @{
    task = @{
        comment = $Comment;
        status = @{
            name = $Status;
        owner = @{
            name = $Owner;
        }
    }
}

我希望函数尽可能动态,因此我将标识符设为强制,然后将所有其他参数设为可选。

Param(
    [Parameter(Mandatory=$true)]
    $TaskID,
    $Comment,
    $Status,
    $Owner
)

如果参数为 NULL,我想从定义中删除该元素,这样它就不会将 NULL 传递给 API。我有下面的逻辑。

If([String]::IsNullOrEmpty($Comment)) {
    $baseTask.task.Remove(‘comment’)
}

If([String]::IsNullOrEmpty($Status)) {
    $baseTask.task.Remove(‘status’)
}

If([String]::IsNullOrEmpty($Owner)) {
    $baseTask.task.Remove(‘owner’)
}

这行得通;但是,如果我有 50 个参数,那么我将通过空值检查进行大量重复,所以我觉得必须有一种更动态的方式来理想地使用某种循环来做到这一点。

我想过一个哈希表,其中包含变量名和要运行的命令之间的映射,但我找不到在执行期间获取变量名的方法。

任何帮助将不胜感激。

【问题讨论】:

  • $PSCmdlet.MyInvocation.MyCommand.Parameters.Keys | Where-Object { $_ -notin $PSBoundParameters.Keys } | ForEach-Object { $baseTask.task.Remove($_) }
  • 谢谢丹尼尔。会看看这个。
  • 非常聪明,@Daniel - 你为什么不把它写下来作为答案呢? (您可以使用$MyInvocation...,这也使解决方案在非高级功能中起作用)。

标签: json powershell


【解决方案1】:

您可以通过将绑定参数与命令的所有可用参数进行比较来检查哪些参数未使用,然后使用它从哈希表中删除不同的元素。

function New-DynamicJson {
    Param(
        [Parameter(Mandatory = $true)]
        $TaskID,
        $Comment,
        $Status,
        $Owner
    )

    $baseTask = @{
        task = [ordered]@{
            Id      = $TaskID;
            comment = $Comment;
            status  = @{
                name = $Status;
            }
            owner   = @{
                name = $Owner;
            }
        }
    }

    $commonParams = 'Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable',
                    'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable'


    $MyInvocation.MyCommand.Parameters.Keys.Where({ $_ -notin $commonParams }) |
        Where-Object { $_ -notin $PSBoundParameters.Keys } |
            ForEach-Object { $baseTask.task.Remove($_) }

    $baseTask | ConvertTo-Json
}

输出

PS> New-DynamicJson -TaskID 103 -Comment 'some comment'

{
    "task":  {
                 "Id":  103,
                 "comment":  "some comment"
             }
}

(感谢mklement0 建议使用$MyInvocation 而不是$PSCmdlet.MyInvocation 这也使解决方案在非高级功能中也能工作

【讨论】:

    【解决方案2】:

    您可以小心处理类似的事情。 这个想法是将名称('status')转换为它应该在的路径('status.name'),然后得到一个像

    这样的哈希表
    'id' = 22
    'status.name' = 'new'
    'comment' = 'text'
    'owner.name' = 'user'
    

    然后将其转换为基于“点”分隔符的多层哈希表:

    $baseTask = @{
        task = @{
            comment = $Comment;
            status = @{
                name = $Status;
            owner = @{
                name = $Owner;
            }
        }
    }
    

    然后是 JSON


    下面的例子是一个例子,应该谨慎使用,因为它有一些问题。

    function test {
        [CMDLetBinding()]
        Param(
            [Parameter(Mandatory=$true)]
            $TaskID,
            $Comment,
            $Status,
            $Owner
    )
        $paramList = @{}
        $params = $PSCmdlet.MyInvocation.BoundParameters
        foreach ($paramName in $params.Keys) {
            switch -Exact  ($paramName) {
                'TaskID'  { $paramList['id'] = $params[$paramName] }
                'Comment' { $paramList['comment'] = $params[$paramName] }
                'Status'  { $paramList['status.name'] = $params[$paramName] }
                'Owner'   { $paramList['owner.name']  = $params[$paramName] }
            }
        }
        $replaced = 0
        do {
            $replaced = 0
            $keys = @($paramList.Keys)
            foreach ($key in $keys) {
                $dotPos = $key.LastIndexOf('.')
                if ($dotPos -gt 0) {
                    $value =  $paramList[$key]
                    if (-not $value.GetType().IsValueType) {
                        if ($value.Clone -ne $null) {
                            $value = $value.Clone()
                        }
                    }
                    $subKey = $key.Substring($dotPos + 1)
                    $newKey = $key.Substring(0, $dotPos)
                    $paramList.Remove($key) | Out-Null
                    $newVal = @{$subKey=$value}
                    $paramList.Add($newKey, $newVal)
                    $replaced++
                }
            }
        } while ($replaced -gt 0)
        return $paramList
        
    }
    
    test -TaskID 123 -Status 'New' -Comment 'Test' -Owner 'user' | ConvertTo-Json -Depth 10 -Compress:$false
    

    【讨论】:

    • 感谢您的回答 filimonic,但这不只是意味着我必须为每个参数编写一个 switch 语句而不是空检查吗?
    • 你必须以某种方式描述每个参数,因为你对它们有不同的处理:“状态”变成“状态\名称”,但“评论”不会变成“评论\名称”,等等。当然可以为您之前未指定的参数使用一些“默认值”。
    猜你喜欢
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 2015-04-03
    • 1970-01-01
    相关资源
    最近更新 更多