How to Write JSON to a File in PowerShell (7 and 5.1)

To write JSON to a file in PowerShell, convert the object with ConvertTo-Json and pipe the text to Set-Content. Raise -Depth when the data is nested:

$data | ConvertTo-Json -Depth 10 | Set-Content -Path .\data.json

That one line hides three traps: cut-off nested data, the wrong file encoding in Windows PowerShell 5.1, and appending that breaks the file. I ran each one in PowerShell 7 and 5.1, and the fixes are below.

Save a PowerShell object as a JSON file

Start with an ordered hashtable, so the keys stay in the order you typed them. Then convert it and save it:

$office = [ordered]@{
    Name   = 'Austin Office'
    Opened = '2026-10-19'
    Budget = 1250
    Staff  = @('Maria Lopez', 'Tom Baker')
}

$office | ConvertTo-Json | Set-Content -Path .\office.json
Get-Content .\office.json -Raw

Output:

{
  "Name": "Austin Office",
  "Opened": "2026-10-19",
  "Budget": 1250,
  "Staff": [
    "Maria Lopez",
    "Tom Baker"
  ]
}
ConvertTo-Json piped to Set-Content writes office.json in PowerShell 7
ConvertTo-Json | Set-Content, then Get-Content -Raw to check the file (PowerShell 7)

Set-Content replaces the file each time. Windows PowerShell 5.1 writes the same data with wider indentation, which is valid JSON but looks different.

A [pscustomobject] works the same way, and so does an array of custom objects. Every property becomes a JSON key.

Read the JSON file back to check it

Always read the file back once. Get-Content -Raw returns one string, and ConvertFrom-Json turns it into an object again:

$o = Get-Content .\office.json -Raw | ConvertFrom-Json
"$($o.Name) opened $($o.Opened) with $($o.Staff.Count) people"

Output:

Austin Office opened 2026-10-19 with 2 people

If this line fails, the file isn’t valid JSON. For lists of records, here’s how to read a JSON file into an array.

Why is my JSON file missing nested data?

ConvertTo-Json stops at a depth of 2 by default. Anything deeper gets flattened into a string. Here, Skills is three levels down:

$team = [ordered]@{
    Office = 'Denver'
    Staff  = @( [ordered]@{ Name = 'Dana Reyes'; Skills = @('Azure', 'SQL') } )
}
$team | ConvertTo-Json -Compress -WarningVariable w -WarningAction SilentlyContinue
"Warnings: $($w.Count)"
$team | ConvertTo-Json -Compress -Depth 5

Output:

{"Office":"Denver","Staff":[{"Name":"Dana Reyes","Skills":"Azure SQL"}]}
Warnings: 1
{"Office":"Denver","Staff":[{"Name":"Dana Reyes","Skills":["Azure","SQL"]}]}
ConvertTo-Json default depth flattens a nested array in PowerShell 7
Default depth turns Skills into “Azure SQL”; -Depth 5 keeps the array (PowerShell 7)

PowerShell 7 writes a warning, so I counted it with -WarningVariable (more on the PowerShell warning stream). Windows PowerShell 5.1 printed Warnings: 0 and saved the broken text without a word.

Pick a -Depth higher than your data needs; the maximum is 100. The ConvertTo-Json cmdlet guide covers -Compress and the other switches.

Set-Content vs Out-File: which encoding do you get?

This is where the two versions really split. I wrote the same JSON, with an accented “é”, three ways and checked the first bytes:

$json = @{ City = 'San José' } | ConvertTo-Json -Compress
$json | Set-Content .\sc.json
$json | Out-File .\of.json
$json | Set-Content .\u8.json -Encoding utf8

foreach ($f in 'sc', 'of', 'u8') {
    $b = [IO.File]::ReadAllBytes("$PWD\$f.json")
    $hex = ($b[0..2] | ForEach-Object { '{0:X2}' -f $_ }) -join ' '
    '{0}.json  {1} bytes  starts {2}' -f $f, $b.Length, $hex
}

Output:

sc.json  22 bytes  starts 7B 22 43
of.json  22 bytes  starts 7B 22 43
u8.json  22 bytes  starts 7B 22 43
JSON file encoding from Set-Content and Out-File in Windows PowerShell 5.1
Set-Content writes ANSI, Out-File writes UTF-16, and -Encoding utf8 adds a BOM (Windows PowerShell 5.1)

In PowerShell 7, all three files are identical UTF-8 without a byte order mark (BOM). That’s the default for both cmdlets.

In 5.1, Set-Content saved the é as a single ANSI byte, Out-File wrote UTF-16 (FF FE) at twice the size, and -Encoding utf8 added the EF BB BF BOM. Some JSON parsers reject that BOM.

PowerShell 7 can still write a BOM with -Encoding utf8BOM. More detail is in writing UTF-8 files in PowerShell and the Out-File cmdlet.

Write BOM-less UTF-8 JSON in Windows PowerShell 5.1

Windows PowerShell 5.1 has no utf8NoBOM value. Call .NET with a UTF8Encoding that turns the BOM off, and pass a full path:

$json = @{ City = 'San José' } | ConvertTo-Json -Compress
$utf8 = [Text.UTF8Encoding]::new($false)
[IO.File]::WriteAllText("$PWD\city.json", $json, $utf8)

$b = [IO.File]::ReadAllBytes("$PWD\city.json")
"$($b.Length) bytes, starts $('{0:X2}' -f $b[0])"

Output:

20 bytes, starts 7B

The file is plain UTF-8: the é takes two bytes and the first byte is the opening brace. It gives the same result in both versions. Here’s more on converting between bytes and strings.

Keep a single item as a JSON array

Piping an array unrolls it, so one item comes out as a plain object. Pass the array to -InputObject instead:

$list = @([pscustomobject]@{ Name = 'Dana Reyes'; City = 'Seattle' })

$list | ConvertTo-Json -Compress
ConvertTo-Json -InputObject $list -Compress

Output:

{"Name":"Dana Reyes","City":"Seattle"}
[{"Name":"Dana Reyes","City":"Seattle"}]

PowerShell 7 also has -AsArray, which wraps the output in brackets, but Windows PowerShell 5.1 doesn’t support it. To force any value into a list first, see converting an object to an array.

How do I append to a JSON file in PowerShell?

Don’t use Add-Content. It adds a second JSON document after the first one, and the file s