【问题标题】:How to traverse JSON properties with Powershell如何使用 Powershell 遍历 JSON 属性
【发布时间】:2025-12-29 10:30:07
【问题描述】:

我正在尝试使用 Powershell 访问 JSON 对象的特定属性值。不幸的是,我不知道结构中某些父属性的键,所以我不能直接这样做。此外,由于 JSON 不是数组,我无法通过索引位置访问。

上下文是我正在从 elasticsearch 查询正在运行的任务列表,并且需要获取任务的 ID(我知道只有一个),以便我可以进行后续调用以发现其完成状态。

我研究了一些查询方法,但不确定如何应用它们(PowerShell 语法对我来说很新)。

我正在使用的 JSON 响应如下所示;

  "nodes" : {
    "oTUltX4IQMOUUVeiohTt8A" : {
      "name" : "H5dfFeA",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1:9300",
      "tasks" : {
        "oTUltX4IQMOUUVeiohTt8A:124" : {
          "node" : "oTUltX4IQMOUUVeiohTt8A",
          "id" : 124,
          "type" : "direct",
          "action" : "cluster:monitor/tasks/lists[n]",
          "start_time_in_millis" : 1458585884904,
          "running_time_in_nanos" : 47402,
          "cancellable" : false,
          "parent_task_id" : "oTUltX4IQMOUUVeiohTt8A:123"
        }
      }
    }
  }
}

有了这个给定的结构,我希望能够访问第一个“任务”的 ID 属性。

所以如果我知道道具键,那就是:

nodes.oTUltX4IQMOUUVeiohTt8A.tasks.oTUltX4IQMOUUVeiohTt8A:124.id

如何在事先不知道密钥的情况下访问此值?

非常感谢任何帮助。

谢谢 尼克

【问题讨论】:

  • 在PowerShell 6“-AsHastable”中,可以直接反序列化为简单的hastable类型,轻松支持key枚举。不要问我为什么几年前没有实现它。但是请注意,每个 JSON 确实没有“第一个”对象,出于兼容性原因,不应使用此类对象。 (一个例外是维护人类偏好排序的工具,这与信息交换不同!)
  • 写。上图:如果数据来自外部来源,请为“第一”或其他顺序确定更可靠的方法。
  • @user2864740:虽然获取属性名称的哈希表表示可能稍微容易一些,但这不是必需的:使用.psobject.properties.Name

标签: json powershell elasticsearch


【解决方案1】:

以下代码定义并使用函数Get-FirstPropertyValue,该函数执行递归,深度优先搜索对象图中具有给定名称的第一个属性并返回其值,假设该值为非空:

# Function that returns the value of the first property with the given 
# name found during recursive depth-first traversal of the given object.
# Note that null-valued properties are ignored.
function Get-FirstPropertyValue($obj, $propName) {
  $propNames = $obj.psobject.properties.Name
  if ($propName -in $propNames) {
    $obj.$propName
  } else {
    foreach ($iterPropName in $propNames) {
        if ($null -ne ($val = Get-FirstPropertyValue $obj.$iterPropName $propName)) {
          return $val
        }
      }
  }
}

# Input JSON
$json = @'
{
    "nodes": {
        "oTUltX4IQMOUUVeiohTt8A": {
            "name": "H5dfFeA",
            "transport_address": "127.0.0.1:9300",
            "host": "127.0.0.1",
            "ip": "127.0.0.1:9300",
            "tasks": {
                "oTUltX4IQMOUUVeiohTt8A:124": {
                    "node": "oTUltX4IQMOUUVeiohTt8A",
                    "id": 124,
                    "type": "direct",
                    "action": "cluster:monitor/tasks/lists[n]",
                    "start_time_in_millis": 1458585884904,
                    "running_time_in_nanos": 47402,
                    "cancellable": false,
                    "parent_task_id": "oTUltX4IQMOUUVeiohTt8A:123"
                }
            }
        }
    }
}
'@

# Convert the JSON to a [pscustomobject] graph with ConvertFrom-Json.
$objFromJson = $json | ConvertFrom-Json

# Using the function defined above, get the first 'tasks' object found
# during recursive depth-first traversal.
$tasks = Get-FirstPropertyValue $objFromJson 'tasks'

# Get the name of the resulting object's first property.
$propName = @($tasks.psobject.properties.Name)[0]

# Extract the .id property from the object stored in the first property.
$tasks.$propName.id

以上产出:

124

一个更简洁,但更模糊,可能更慢的替代方法将 JSON 输入转换为 XML,然后使用 XPath 来查询它:

# Input JSON
$json = @'
{
    "nodes": {
        "oTUltX4IQMOUUVeiohTt8A": {
            "name": "H5dfFeA",
            "transport_address": "127.0.0.1:9300",
            "host": "127.0.0.1",
            "ip": "127.0.0.1:9300",
            "tasks": {
                "oTUltX4IQMOUUVeiohTt8A:124": {
                    "node": "oTUltX4IQMOUUVeiohTt8A",
                    "id": 124,
                    "type": "direct",
                    "action": "cluster:monitor/tasks/lists[n]",
                    "start_time_in_millis": 1458585884904,
                    "running_time_in_nanos": 47402,
                    "cancellable": false,
                    "parent_task_id": "oTUltX4IQMOUUVeiohTt8A:123"
                }
            }
        }
    }
}
'@

$parent = 'tasks'
$prop = 'id'
$propType = 'int'

$json | 
  ConvertFrom-Json |
    ConvertTo-Xml -Depth ([int]::MaxValue) | 
      Select-Xml "//Property[@Name='$parent']/*/*[@Name='$prop']/text()" |
        ForEach-Object { $_.Node.InnerText -as $propType }

【讨论】:

  • 谢谢我有这个工作:0 虽然我必须允许元素不存在,在这种情况下,上述方法将永远递归,所以我对最大递归进行了限制,这很好我的用例。
  • 很高兴听到这个消息,@Grenville。因为如果有问题我想修复我的Get-FirstPropertyValue 函数:当给定一个不存在的属性名称时,我看不到它无限期地递归。你能详细说明什么时候会发生这种情况吗?
【解决方案2】:

据我所知,有两种方法可以实现这一点,但看起来都有些粗糙。

对于这些示例,我会将您提供的 JSON 加载到 $json

$json = @'
{
    "nodes": {
        "oTUltX4IQMOUUVeiohTt8A": {
            "name": "H5dfFeA",
            "transport_address": "127.0.0.1:9300",
            "host": "127.0.0.1",
            "ip": "127.0.0.1:9300",
            "tasks": {
                "oTUltX4IQMOUUVeiohTt8A:124": {
                    "node": "oTUltX4IQMOUUVeiohTt8A",
                    "id": 124,
                    "type": "direct",
                    "action": "cluster:monitor/tasks/lists[n]",
                    "start_time_in_millis": 1458585884904,
                    "running_time_in_nanos": 47402,
                    "cancellable": false,
                    "parent_task_id": "oTUltX4IQMOUUVeiohTt8A:123"
                }
            }
        }
    }
}
'@ | ConvertFrom-Json

第一种是使用Select-Object选择第一项,然后展开属性。

(($json.nodes | Select-Object -First 1 -ExpandProperty *).tasks | Select-Object -First 1 -ExpandProperty *).id

更强大的方法是使用隐藏的 PSObject 属性 Value,因为 JSON 被 PowerShell 解析为 PSCustomObject。

PS C:\Windows\system32> $json.nodes.GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                                                                      
-------- -------- ----                                     --------                                                                                                                                                      
True     False    PSCustomObject                           System.Object  

.PSObject 的属性

PS C:\Windows\system32> $json.nodes.PSObject.Properties


MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : @{name=H5dfFeA; transport_address=127.0.0.1:9300; host=127.0.0.1; ip=127.0.0.1:9300; tasks=}
TypeNameOfValue : Selected.System.Management.Automation.PSCustomObject
Name            : oTUltX4IQMOUUVeiohTt8A
IsInstance      : True

获取ID值的完整命令:

$json.nodes.PSObject.Properties.Value.tasks.PSObject.Properties.Value.id

【讨论】: