在powershell中为数组中的每个json对象添加特定属性

时间:2022-01-19 20:21:29

I have an array of JSON objects and I want to add a particular property to each object present.

我有一个JSON对象数组,我想为每个存在的对象添加一个特定的属性。

For example, the array is as follows:

例如,数组如下:

[
    {
        "Average":  1.3085,
        "ExtendedStatistics":  {

                               },
        "Maximum":  0,
        "Minimum":  0,
        "SampleCount":  0,
        "Sum":  0,
        "Timestamp":  "\/Date(1496972280000)\/",
        "Unit":  {
                     "Value":  "Percent"
                 }
    },
    {
        "Average":  1.4324999999999999,
        "ExtendedStatistics":  {

                               },
        "Maximum":  0,
        "Minimum":  0,
        "SampleCount":  0,
        "Sum":  0,
        "Timestamp":  "\/Date(1496944680000)\/",
        "Unit":  {
                     "Value":  "Percent"
                 }
    }
]

I want to add "source": "CPU" to all objects. How do I go about doing that? I am new to PowerShell and haven't been able to get this done.

我想为所有对象添加“source”:“CPU”。我该怎么做呢?我是PowerShell的新手并且无法完成这项工作。

2 个解决方案

#1


5  

You could do the following:

您可以执行以下操作:

$JSON | ConvertFrom-Json | ForEach-Object { 
    $_ | Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru
} | ConvertTo-Json

This assumes your JSON input is in a variable named $JSON, you'll need to replace this with however you access your JSON content (e.g Get-Content yourfile.json).

这假设您的JSON输入位于名为$ JSON的变量中,您需要将其替换为您访问JSON内容(例如Get-Content yourfile.json)。

Once you have the JSON, we use ConvertFrom-JSON to convert it to a PowerShell object.

获得JSON后,我们使用ConvertFrom-JSON将其转换为PowerShell对象。

We then use the pipeline to send this to a ForEach-Object loop which uses the Add-Member cmdlet to add a property to each item in the collection (the current item is represented by $_) named 'Source' with a value of 'CPU'. Per the comments from mklement0, it is necessary to use the -PassThru switch to send the result back to the pipeline.

然后,我们使用管道将此发送到ForEach-Object循环,该循环使用Add-Member cmdlet将属性添加到集合中的每个项目(当前项由$ _表示),名为“Source”,值为'*处理器'。根据mklement0的注释,有必要使用-PassThru开关将结果发送回管道。

Then we pipe that output to ConvertTo-JSON to convert it back.

然后我们将该输出传递给ConvertTo-JSON以将其转换回来。

#2


4  

Mark Wragg's helpful answer works well, but you may wonder why the Add-Member cmdlet cannot be piped to directly, as opposed to requiring an enclosing ForEach-Object call:

Mark Wragg的有用答案很有效,但您可能想知道为什么Add-Member cmdlet不能直接用于管道,而不是要求封闭ForEach-Object调用:

Arguably, the following should work, but currently (Windows PowerShell v5.1, PowerShell Core v6.1.0) doesn't:

可以说,以下应该可以工作,但目前(Windows PowerShell v5.1,PowerShell Core v6.1.0)不能:

# !! Currently does NOT work as expected.
$JSON | ConvertFrom-Json | 
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

The idea is that Add-Member uses pipeline input directly, and, after modifying each input object, outputs it, thanks to -PassThru (by default Add-Member produces no output).

想法是Add-Member直接使用管道输入,并且在修改每个输入对象之后输出它,这要归功于-PassThru(默认情况下Add-Member不产生输出)。

The reason that it doesn't work is that when ConvertFrom-Json outputs an array, it outputs it as a single object rather than sending its elements one by one through the pipeline, as one would expect.

它不起作用的原因是当ConvertFrom-Json输出一个数组时,它将它作为单个对象输出,而不是像人们期望的那样逐个通过管道发送它的元素。

This problematic behavior is being discussed in an issue in the PowerShell GitHub repository.

PowerShell GitHub存储库中的一个问题正在讨论这种有问题的行为。

If you agree that it is problematic, make your voice heard there; even a simple "thumbs up" shows that there's interest in getting it changed.

如果您同意这是有问题的,请在那里听到您的声音;甚至一个简单的“竖起大拇指”表明有兴趣改变它。

Workarounds:

  • Use (...), which forces enumeration of the array:
  • 使用(...),强制枚举数组:

# Enclosing the ConvertFrom-Json command in (...) forces enumeration.
($JSON | ConvertFrom-Json) | 
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

Note that, generally, using (...) to force collection of a command's entire output in-memory in an array is convenient, but can be problematic with large output sets. As PetSerAl points out, however, in this case it is fine, because ConvertFrom-Json itself constructs the entire output array in memory up front anyway.

请注意,通常,使用(...)强制在数组中收集命令的整个输出内存是很方便的,但是对于大输出集可能会有问题。正如PetSerAl所指出的那样,在这种情况下它很好,因为无论如何,ConvertFrom-Json本身都会在内存中构建整个输出数组。

  • Alternative: A pass-through call to Write-Output -NoEnumerate (Windows PowerShell) /
    just Write-Output (PowerShell Core), whose sole purpose is to force enumeration of the array elements:
  • 替代方案:对Write-Output -NoEnumerate(Windows PowerShell)/ Just Write-Output(PowerShell Core)的传递调用,其唯一目的是强制枚举数组元素:

# Inserting Write-Output [-NoEnumerate] between ConvertFrom-Json and Add-Member
# forces enumeration of the array elements.

# *Windows PowerShell*, as of v5.1:
$JSON | ConvertFrom-Json | Write-Output -NoEnumerate |
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

# PowerShell *Core*:
$JSON | ConvertFrom-Json | Write-Output |
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

Optional reading: the quirks of Write-Output:

Windows PowerShell as of v5.1:

Windows PowerShell从v5.1开始:

Due to a bug, Write-Output invariably enumerates single objects that are collections themselves, even when you add -NoEnumerate.

由于存在错误,即使添加-NoEnumerate,Write-Output也总是枚举集合本身的单个对象。

Paradoxically, -NoEnumerate is actually needed in this case - even though we do want to enumerate! - so as to prevent Write-Output from applying enumeration twice: once (invariably) to the input array, and again to the individual array elements (thanks again, PetSerAl); e.g.:

矛盾的是,在这种情况下实际上需要-NoEnumerate - 即使我们确实想要枚举! - 以防止Write-Output两次应用枚举:一次(总是)到输入数组,再次到各个数组元素(再次感谢PetSerAl);例如。:

# !! Unexpectedly returns 4(!): enumerates the outer 2-element array
# !! *and* its elements.
# (In PowerShell *Core*, this yields 2, as expected.)
Write-Output -InputObject (1, 2), (3, 4) | Measure-Object

# BETTER: yields 2, because only the outer 2-element array is enumerated
# (In PowerShell *Core*, this yields 1, as expected.)
Write-Output -NoEnumerate -InputObject (1, 2), (3, 4) | Measure-Object

PowerShell Core:

The above problem has been fixed, which means that - sensibly - you mustn't use -NoEnumerate if you do want Write-Output to enumerate pipeline objects that are themselves collections (and enumeration no longer recurses 1 level).

上面的问题已得到修复,这意味着 - 合理地 - 如果你想让Write-Output枚举本身是集合的管道对象(并且枚举不再递归1级),你就不能使用-NoEnumerate。

Unfortunately, a new bug was introduced: a single scalar input object is unexpectedly wrapped in a [PSObject[]] array:

不幸的是,引入了一个新的错误:单个标量输入对象意外地包装在[PSObject []]数组中:

> (1 | Write-Output -NoEnumerate).GetType().Name
PSObject[]

PetSerAl has reported this bug here.

PetSerAl在这里报告了这个错误。

#1


5  

You could do the following:

您可以执行以下操作:

$JSON | ConvertFrom-Json | ForEach-Object { 
    $_ | Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru
} | ConvertTo-Json

This assumes your JSON input is in a variable named $JSON, you'll need to replace this with however you access your JSON content (e.g Get-Content yourfile.json).

这假设您的JSON输入位于名为$ JSON的变量中,您需要将其替换为您访问JSON内容(例如Get-Content yourfile.json)。

Once you have the JSON, we use ConvertFrom-JSON to convert it to a PowerShell object.

获得JSON后,我们使用ConvertFrom-JSON将其转换为PowerShell对象。

We then use the pipeline to send this to a ForEach-Object loop which uses the Add-Member cmdlet to add a property to each item in the collection (the current item is represented by $_) named 'Source' with a value of 'CPU'. Per the comments from mklement0, it is necessary to use the -PassThru switch to send the result back to the pipeline.

然后,我们使用管道将此发送到ForEach-Object循环,该循环使用Add-Member cmdlet将属性添加到集合中的每个项目(当前项由$ _表示),名为“Source”,值为'*处理器'。根据mklement0的注释,有必要使用-PassThru开关将结果发送回管道。

Then we pipe that output to ConvertTo-JSON to convert it back.

然后我们将该输出传递给ConvertTo-JSON以将其转换回来。

#2


4  

Mark Wragg's helpful answer works well, but you may wonder why the Add-Member cmdlet cannot be piped to directly, as opposed to requiring an enclosing ForEach-Object call:

Mark Wragg的有用答案很有效,但您可能想知道为什么Add-Member cmdlet不能直接用于管道,而不是要求封闭ForEach-Object调用:

Arguably, the following should work, but currently (Windows PowerShell v5.1, PowerShell Core v6.1.0) doesn't:

可以说,以下应该可以工作,但目前(Windows PowerShell v5.1,PowerShell Core v6.1.0)不能:

# !! Currently does NOT work as expected.
$JSON | ConvertFrom-Json | 
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

The idea is that Add-Member uses pipeline input directly, and, after modifying each input object, outputs it, thanks to -PassThru (by default Add-Member produces no output).

想法是Add-Member直接使用管道输入,并且在修改每个输入对象之后输出它,这要归功于-PassThru(默认情况下Add-Member不产生输出)。

The reason that it doesn't work is that when ConvertFrom-Json outputs an array, it outputs it as a single object rather than sending its elements one by one through the pipeline, as one would expect.

它不起作用的原因是当ConvertFrom-Json输出一个数组时,它将它作为单个对象输出,而不是像人们期望的那样逐个通过管道发送它的元素。

This problematic behavior is being discussed in an issue in the PowerShell GitHub repository.

PowerShell GitHub存储库中的一个问题正在讨论这种有问题的行为。

If you agree that it is problematic, make your voice heard there; even a simple "thumbs up" shows that there's interest in getting it changed.

如果您同意这是有问题的,请在那里听到您的声音;甚至一个简单的“竖起大拇指”表明有兴趣改变它。

Workarounds:

  • Use (...), which forces enumeration of the array:
  • 使用(...),强制枚举数组:

# Enclosing the ConvertFrom-Json command in (...) forces enumeration.
($JSON | ConvertFrom-Json) | 
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

Note that, generally, using (...) to force collection of a command's entire output in-memory in an array is convenient, but can be problematic with large output sets. As PetSerAl points out, however, in this case it is fine, because ConvertFrom-Json itself constructs the entire output array in memory up front anyway.

请注意,通常,使用(...)强制在数组中收集命令的整个输出内存是很方便的,但是对于大输出集可能会有问题。正如PetSerAl所指出的那样,在这种情况下它很好,因为无论如何,ConvertFrom-Json本身都会在内存中构建整个输出数组。

  • Alternative: A pass-through call to Write-Output -NoEnumerate (Windows PowerShell) /
    just Write-Output (PowerShell Core), whose sole purpose is to force enumeration of the array elements:
  • 替代方案:对Write-Output -NoEnumerate(Windows PowerShell)/ Just Write-Output(PowerShell Core)的传递调用,其唯一目的是强制枚举数组元素:

# Inserting Write-Output [-NoEnumerate] between ConvertFrom-Json and Add-Member
# forces enumeration of the array elements.

# *Windows PowerShell*, as of v5.1:
$JSON | ConvertFrom-Json | Write-Output -NoEnumerate |
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

# PowerShell *Core*:
$JSON | ConvertFrom-Json | Write-Output |
  Add-Member -MemberType NoteProperty -Name 'Source' -Value 'CPU' -PassThru 

Optional reading: the quirks of Write-Output:

Windows PowerShell as of v5.1:

Windows PowerShell从v5.1开始:

Due to a bug, Write-Output invariably enumerates single objects that are collections themselves, even when you add -NoEnumerate.

由于存在错误,即使添加-NoEnumerate,Write-Output也总是枚举集合本身的单个对象。

Paradoxically, -NoEnumerate is actually needed in this case - even though we do want to enumerate! - so as to prevent Write-Output from applying enumeration twice: once (invariably) to the input array, and again to the individual array elements (thanks again, PetSerAl); e.g.:

矛盾的是,在这种情况下实际上需要-NoEnumerate - 即使我们确实想要枚举! - 以防止Write-Output两次应用枚举:一次(总是)到输入数组,再次到各个数组元素(再次感谢PetSerAl);例如。:

# !! Unexpectedly returns 4(!): enumerates the outer 2-element array
# !! *and* its elements.
# (In PowerShell *Core*, this yields 2, as expected.)
Write-Output -InputObject (1, 2), (3, 4) | Measure-Object

# BETTER: yields 2, because only the outer 2-element array is enumerated
# (In PowerShell *Core*, this yields 1, as expected.)
Write-Output -NoEnumerate -InputObject (1, 2), (3, 4) | Measure-Object

PowerShell Core:

The above problem has been fixed, which means that - sensibly - you mustn't use -NoEnumerate if you do want Write-Output to enumerate pipeline objects that are themselves collections (and enumeration no longer recurses 1 level).

上面的问题已得到修复,这意味着 - 合理地 - 如果你想让Write-Output枚举本身是集合的管道对象(并且枚举不再递归1级),你就不能使用-NoEnumerate。

Unfortunately, a new bug was introduced: a single scalar input object is unexpectedly wrapped in a [PSObject[]] array:

不幸的是,引入了一个新的错误:单个标量输入对象意外地包装在[PSObject []]数组中:

> (1 | Write-Output -NoEnumerate).GetType().Name
PSObject[]

PetSerAl has reported this bug here.

PetSerAl在这里报告了这个错误。