PowerShell Working with JSON: ConvertTo-Json and ConvertFrom-Json

PowerShell Working with JSON: ConvertTo-Json and ConvertFrom-Json

PowerShell Tips Editor 2 min read
PowerShell Working with JSON: ConvertTo-Json and ConvertFrom-Json

REST APIs, configuration files, and inter-service communication all use JSON. PowerShell handles it natively without any external libraries — convert objects to JSON, parse JSON from API responses, read JSON config files, and write them back. This guide covers everything you need to work with PowerShell JSON: ConvertTo-Json, ConvertFrom-Json, depth handling, file I/O, and accessing nested properties.

Quick Answer / TL;DR

# Object to JSON
$obj = [PSCustomObject]@{ Name='web01'; Port=443 }
$obj | ConvertTo-Json

# JSON string to object
'{"Name":"web01","Port":443}' | ConvertFrom-Json

ConvertTo-Json: Objects to JSON

Pipe any PowerShell object to ConvertTo-Json to serialize it:

# Simple object
[PSCustomObject]@{
    Server  = 'web01.corp.local'
    Port    = 443
    Enabled = $true
} | ConvertTo-Json

# Array of objects
@(
    [PSCustomObject]@{ Name='web01'; Status='Running' },
    [PSCustomObject]@{ Name='web02'; Status='Stopped' }
) | ConvertTo-Json

# Hashtable
@{ Timeout=30; Retries=3; Debug=$false } | ConvertTo-Json -Compress
{
    "Server":  "web01.corp.local",
    "Port":  443,
    "Enabled":  true
}

{"Timeout":30,"Retries":3,"Debug":false}

-Compress produces minified single-line JSON, useful for API payloads and writing to files. Without it, the output is pretty-printed with indentation.

ConvertFrom-Json: Parse JSON String

Pass a JSON string to ConvertFrom-Json to get a PowerShell object:

$jsonString = '{"Server":"db01","Port":5432,"Databases":["AppDB","LogDB"]}'

$obj = $jsonString | ConvertFrom-Json

$obj.Server           # db01
$obj.Port             # 5432
$obj.Databases        # AppDB, LogDB
$obj.Databases[0]     # AppDB

# Type of the result
$obj.GetType().Name   # PSCustomObject
db01
5432
AppDB
LogDB
AppDB
PSCustomObject

ConvertFrom-Json returns a PSCustomObject, not a hashtable. Properties are accessible with dot notation. JSON arrays become PowerShell arrays.

Controlling Depth with -Depth

The -Depth parameter controls how many levels of nested objects are serialized. The default is 2, which truncates deeply nested data:

$nested = [PSCustomObject]@{
    Level1 = [PSCustomObject]@{
        Level2 = [PSCustomObject]@{
            Level3 = [PSCustomObject]@{
                Value = 'Deep data'
            }
        }
    }
}

# Default depth 2 — Level3 gets truncated
$nested | ConvertTo-Json

# Full depth
$nested | ConvertTo-Json -Depth 10
# Default depth=2:
{
    "Level1":  {
        "Level2":  "@{Level3=}"  <-- truncated to string!
    }
}

# With -Depth 10:
{
    "Level1":  {
        "Level2":  {
            "Level3":  {
                "Value":  "Deep data"
            }
        }
    }
}

Always set -Depth explicitly when working with complex objects. The truncation is silent — you get a string representation of the nested object rather than an error, which is easy to miss.

Read JSON from a File

Combine Get-Content -Raw with ConvertFrom-Json:

# Read a JSON config file
$config = Get-Content 'C:\Config\appsettings.json' -Raw | ConvertFrom-Json

$config.Database.Server
$config.Database.Port

# Read a JSON array from file
$servers = Get-Content 'C:\Config\servers.json' -Raw | ConvertFrom-Json
$servers.Count
$servers | Where-Object { $_.Environment -eq 'Production' }
db01.corp.local
5432
5

The -Raw flag is essential — without it, Get-Content returns an array of strings (one per line) rather than a single JSON string, which causes ConvertFrom-Json to fail.

Write JSON to a File

$config = [PSCustomObject]@{
    Database = [PSCustomObject]@{
        Server   = 'db01.corp.local'
        Port     = 5432
        Database = 'Production'
    }
    Logging  = [PSCustomObject]@{
        Level  = 'Info'
        Path   = 'C:\Logs\app.log'
    }
}

# Write pretty-printed JSON
$config | ConvertTo-Json -Depth 5 |
    Set-Content 'C:\Config\appsettings.json' -Encoding UTF8

# Write minified JSON
$config | ConvertTo-Json -Depth 5 -Compress |
    Set-Content 'C:\Config\appsettings.min.json' -Encoding UTF8

Access Nested Properties

Navigate nested JSON structures with chained dot notation:

$apiResponse = '{
    "status": "ok",
    "data": {
        "users": [
            {"id": 1, "name": "Alice", "role": "admin"},
            {"id": 2, "name": "Bob",   "role": "viewer"}
        ],
        "total": 2
    }
}' | ConvertFrom-Json

$apiResponse.status             # ok
$apiResponse.data.total         # 2
$apiResponse.data.users[0].name # Alice

# Filter nested array
$apiResponse.data.users |
    Where-Object { $_.role -eq 'admin' } |
    Select-Object name, role
ok
2
Alice

name  role
----  ----
Alice admin

Common Errors and Fixes

  • Default -Depth 2 truncates nested objects: If your JSON output shows something like "@{PropertyName=}" instead of nested JSON, the default depth of 2 was exceeded. Always set -Depth 10 (or higher) when working with complex nested objects.
  • JSON arrays return PSCustomObject not hashtable: ConvertFrom-Json returns PSCustomObject instances, not hashtables. You can’t use .ContainsKey() — use if ($obj.PSObject.Properties['key']) to check if a property exists on a PSCustomObject.

Related Cmdlets / See Also

Wrapping Up

PowerShell handles JSON natively with ConvertTo-Json and ConvertFrom-Json. Always use -Depth 10 for nested objects, Get-Content -Raw when reading JSON files, and -Encoding UTF8 when writing. Access properties with dot notation and filter arrays with Where-Object. Your next step: read a real API’s JSON response and filter it down to just the fields you need.

Send-Item -To