Custom objects, JSON and CSV

PowerShell · Practical · 12 min

Real scripts read data from files and APIs, reshape it and write reports.

[pscustomobject]@{ Name = 'web1'; CPU = 72 } creates an object with those properties, in that order. Custom objects flow through the pipeline like any cmdlet output: you can filter, sort, group and export them. They are the right way to return structured results from your functions, instead of formatted strings.

JSON: ConvertTo-Json serializes objects (mind -Depth: the default of 2 truncates deeper nesting, with a warning), and ConvertFrom-Json parses text into objects; -AsHashtable gives hashtables instead. Read a file with Get-Content file.json -Raw | ConvertFrom-Json.

CSV: Export-Csv writes objects (PowerShell 7 no longer adds a type header, so -NoTypeInformation is optional), and Import-Csv reads rows back as objects whose property values are all strings. Cast before comparing numbers: [int]$_.CPU -gt 50.

REST: Invoke-RestMethod calls an HTTP API and converts the JSON response into objects automatically; Invoke-WebRequest gives the raw response with status code and headers.

To add a property to an existing object use Add-Member or Select-Object *, @{ Name = 'New'; Expression = { ... } }. ForEach-Object Name (member name shorthand) pulls a single property out of each object.

Example

Set-Location ([IO.Path]::GetTempPath())
$servers = @(
  [pscustomobject]@{ Name = 'web1'; CPU = 72; Env = 'prod' }
  [pscustomobject]@{ Name = 'web2'; CPU = 15; Env = 'prod' }
  [pscustomobject]@{ Name = 'dev1'; CPU = 90; Env = 'dev' }
)
$servers | Where-Object CPU -gt 50 | ForEach-Object Name   # web1 / dev1
$json = $servers | ConvertTo-Json -Compress
$json.Substring(0, 38)              # [{"Name":"web1","CPU":72,"Env":"prod"}
$back = $json | ConvertFrom-Json
$back[2].CPU + 1                    # 91
$servers | Export-Csv servers.csv
(Import-Csv servers.csv)[0].CPU.GetType().Name     # String  (CSV is text)
@(Import-Csv servers.csv | Where-Object { [int]$_.CPU -gt 50 }).Count   # 2
$servers | Group-Object Env | ForEach-Object { "$($_.Name): $($_.Count)" }   # dev: 1 / prod: 2  (groups sorted by name)
# $repo = Invoke-RestMethod https://api.github.com/repos/PowerShell/PowerShell
# $repo.stargazers_count          # JSON response arrives as objects

Remember

  • [pscustomobject]@{ } is the standard record type
  • ConvertTo-Json needs -Depth for nested data
  • Import-Csv values are strings; cast before numeric comparisons

Watch out for

  • Comparing Import-Csv values as numbers without casting: '9' -gt '50' is True as strings.

Check yourself

  1. After Import-Csv, `$row.Count -gt 100` behaves oddly. Why?
  2. What can go wrong with `$deep | ConvertTo-Json` on nested data?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-json