Skip to content

Working with JSON

Learn how to parse, create, and manipulate JSON data in PowerShell using ConvertFrom-Json and ConvertTo-Json.

Overview

JSON (JavaScript Object Notation) is a lightweight data format commonly used for APIs, configuration files, and data exchange. PowerShell makes it easy to convert between JSON strings and PowerShell objects, allowing you to work with JSON data naturally.

Common use cases:

  • API request and response handling
  • Configuration file management
  • Data serialization and storage
  • Cross-platform data exchange
  • Web service integration

Basic Syntax

Converting JSON to PowerShell (ConvertFrom-Json)

Convert JSON strings into PowerShell objects:

# Simple JSON string
$json = '{"name":"Raymond","age":30,"active":true}'
$user = $json | ConvertFrom-Json

# Access properties with dot notation
$user.name      # "Raymond"
$user.age       # 30
$user.active    # True

Converting PowerShell to JSON (ConvertTo-Json)

Convert PowerShell objects into JSON strings:

# Create a hashtable
$config = @{
    Server = "db01.company.com"
    Port = 5432
    Timeout = 30
    EnableSSL = $true
}

# Convert to JSON
$json = $config | ConvertTo-Json

# Result:
# {
#   "Server": "db01.company.com",
#   "Port": 5432,
#   "Timeout": 30,
#   "EnableSSL": true
# }

Working with Nested JSON

Handle complex JSON structures:

# Nested JSON example
$json = @"
{
    "user": {
        "name": "Raymond",
        "contact": {
            "email": "raymond@example.com",
            "phone": "555-1234"
        }
    },
    "status": "active"
}
"@

$data = $json | ConvertFrom-Json

# Access nested properties
$data.user.name                  # "Raymond"
$data.user.contact.email         # "raymond@example.com"
$data.user.contact.phone         # "555-1234"
$data.status                     # "active"

Working with JSON Arrays

Work with arrays in JSON:

# JSON with array
$json = @"
{
    "users": [
        {"name": "Alice", "age": 28},
        {"name": "Bob", "age": 32},
        {"name": "Charlie", "age": 25}
    ]
}
"@

$data = $json | ConvertFrom-Json

# Access array elements
$data.users[0].name      # "Alice"
$data.users[1].age       # 32
$data.users.Count        # 3

# Loop through array
foreach ($user in $data.users) {
    Write-Output "$($user.name) is $($user.age) years old"
}

# Filter array
$data.users | Where-Object {$_.age -gt 30}

The -Depth Parameter

Control how deeply nested objects are converted:

# Complex nested object
$config = @{
    Level1 = @{
        Level2 = @{
            Level3 = @{
                Level4 = "Deep value"
            }
        }
    }
}

# Default depth is 2 - deeper levels become strings
$json = $config | ConvertTo-Json
# Level3 and beyond will be truncated!

# Increase depth for deeply nested objects
$json = $config | ConvertTo-Json -Depth 5

# When reading JSON, increase depth if needed
$data = $json | ConvertFrom-Json -Depth 10

Pretty-Printing JSON

Format JSON for readability:

# Default behavior: Pretty-printed (indented)
$data = @{Name="Raymond"; City="St. Louis"; Active=$true}
$data | ConvertTo-Json
# Output (formatted with indentation):
# {
#   "Name": "Raymond",
#   "City": "St. Louis",
#   "Active": true
# }

# Compact JSON (single line) - Use -Compress parameter
$compact = $data | ConvertTo-Json -Compress
# Output: {"Name":"Raymond","City":"St. Louis","Active":true}

# Note: ConvertTo-Json pretty-prints by default
# Use -Compress when you need single-line output (for APIs, storage, etc.)

Reading and Writing JSON Files

Work with JSON files:

# Read JSON from file
$config = Get-Content -Path C:\Config\settings.json -Raw | ConvertFrom-Json

# Access properties
$config.Database.Server
$config.Database.Port

# Modify and save back
$config.Database.Port = 5433
$config | ConvertTo-Json -Depth 10 | Set-Content -Path C:\Config\settings.json

# Create new JSON file
$settings = @{
    AppName = "MyApp"
    Version = "1.0.0"
    Settings = @{
        Theme = "Dark"
        Language = "en-US"
    }
}
$settings | ConvertTo-Json | Out-File -FilePath C:\Config\app.json

Important Parameters

Parameter Cmdlet Description Example
-Depth ConvertTo-Json Maximum nesting levels to convert (default: 2) -Depth 5
-Compress ConvertTo-Json Output as single-line JSON -Compress
-AsHashtable ConvertFrom-Json Return hashtable instead of PSCustomObject (PS 6+) -AsHashtable
-Depth ConvertFrom-Json Maximum nesting levels to parse (default: 1024) -Depth 10
-NoEnumerate ConvertFrom-Json Don't enumerate arrays with single element -NoEnumerate

Common JSON Patterns

# Pattern 1: API response handling
$response = Invoke-RestMethod -Uri "https://api.example.com/users/1"
Write-Output "User: $($response.name), Email: $($response.email)"

# Pattern 2: Configuration file
$config = Get-Content C:\app-config.json -Raw | ConvertFrom-Json
$dbConnection = "Server=$($config.db.server);Port=$($config.db.port)"

# Pattern 3: Building API request body
$payload = @{
    username = "jdoe"
    email = "jdoe@example.com"
    active = $true
} | ConvertTo-Json

Invoke-RestMethod -Uri "https://api.example.com/users" -Method Post -Body $payload -ContentType "application/json"

# Pattern 4: Array of objects to JSON
$servers = @(
    @{Name="Server01"; IP="192.168.1.10"; Role="Web"}
    @{Name="Server02"; IP="192.168.1.11"; Role="Database"}
)
$servers | ConvertTo-Json

Common Use Cases

Use Case 1: Parse API Response

What it does: Convert JSON from web APIs into PowerShell objects for data extraction

# Fetch and parse API response
$response = Invoke-RestMethod -Uri "https://api.github.com/users/octocat"
Write-Output "User: $($response.name), Location: $($response.location)"
Write-Output "Followers: $($response.followers), Repos: $($response.public_repos)"

Use Case 2: Save Configuration Files

What it does: Store application settings in JSON format for easy editing and portability

# Create configuration object
$config = @{
    Database = @{
        Server = "db01.local"
        Port = 5432
        Timeout = 30
    }
    Logging = @{
        Level = "Info"
        Path = "C:\Logs\app.log"
    }
}

# Save to JSON file
$config | ConvertTo-Json -Depth 5 | Out-File "C:\Config\appsettings.json"

Use Case 3: Build API Request Bodies

What it does: Create JSON payloads for POST/PUT requests to REST APIs

# Build request body
$newUser = @{
    firstName = "John"
    lastName = "Doe"
    email = "john.doe@example.com"
    department = "IT"
    active = $true
}

# Convert and send to API
$jsonBody = $newUser | ConvertTo-Json
Invoke-RestMethod -Uri "https://api.example.com/users" -Method Post -Body $jsonBody -ContentType "application/json"

Use Case 4: Data Exchange Between Systems

What it does: Serialize PowerShell data for cross-platform compatibility and storage

# Export system inventory to JSON
$inventory = Get-CimInstance Win32_ComputerSystem | Select-Object Name, Manufacturer, Model, TotalPhysicalMemory
$inventory | ConvertTo-Json | Out-File "C:\Reports\inventory.json"

# Import on another system
$importedData = Get-Content "C:\Reports\inventory.json" -Raw | ConvertFrom-Json
Write-Output "Computer: $($importedData.Name), RAM: $($importedData.TotalPhysicalMemory / 1GB)GB"

Use Case 5: Log Structured Data

What it does: Create machine-readable log entries in JSON format for analysis

# Create structured log entry
$logEntry = @{
    Timestamp = (Get-Date).ToString("o")
    Level = "Error"
    Message = "Database connection failed"
    Details = @{
        Server = "db01.local"
        Port = 5432
        ErrorCode = "ConnectionTimeout"
    }
}

# Append to log file
$logEntry | ConvertTo-Json -Compress | Add-Content "C:\Logs\app-structured.log"

Real-World Examples

Example: Parse API Response

Scenario: Fetch user data from a REST API and extract specific information

# Fetch JSON from API
$response = Invoke-RestMethod -Uri "https://api.github.com/users/octocat"

# Response is automatically converted from JSON
Write-Output "Username: $($response.login)"
Write-Output "Name: $($response.name)"
Write-Output "Public Repos: $($response.public_repos)"
Write-Output "Followers: $($response.followers)"

Example: Configuration Management

Scenario: Load application settings from JSON file

# settings.json content:
# {
#   "database": {
#     "server": "db01.local",
#     "port": 5432,
#     "database": "myapp"
#   },
#   "logging": {
#     "level": "Info",
#     "path": "C:\\Logs\\app.log"
#   }
# }

# Load configuration
$config = Get-Content -Path .\settings.json -Raw | ConvertFrom-Json

# Use configuration values
$connectionString = "Server=$($config.database.server);Port=$($config.database.port);Database=$($config.database.database)"
$logPath = $config.logging.path
$logLevel = $config.logging.level

Write-Output "Connecting to: $connectionString"
Write-Output "Logging to: $logPath at level $logLevel"

Example: Create JSON for API POST Request

Scenario: Send new user data to an API

# Build user object as hashtable
$newUser = @{
    firstName = "John"
    lastName = "Doe"
    email = "john.doe@example.com"
    department = "IT"
    active = $true
    roles = @("user", "contributor")
}

# Convert to JSON
$jsonBody = $newUser | ConvertTo-Json

# Send to API
$response = Invoke-RestMethod -Uri "https://api.example.com/users" `
    -Method Post `
    -Body $jsonBody `
    -ContentType "application/json"

Write-Output "User created with ID: $($response.id)"

Example: Process Array of JSON Objects

Scenario: Parse a JSON file containing multiple records

# employees.json:
# [
#   {"name": "Alice", "dept": "IT", "salary": 75000},
#   {"name": "Bob", "dept": "HR", "salary": 65000},
#   {"name": "Charlie", "dept": "IT", "salary": 80000}
# ]

$employees = Get-Content .\employees.json -Raw | ConvertFrom-Json

# Filter IT employees
$itEmployees = $employees | Where-Object {$_.dept -eq "IT"}

# Calculate average IT salary
$avgSalary = ($itEmployees | Measure-Object -Property salary -Average).Average

Write-Output "IT Employees: $($itEmployees.Count)"
Write-Output "Average IT Salary: `$$avgSalary"

# Export to CSV
$itEmployees | Export-Csv -Path .\it-employees.csv -NoTypeInformation

Tips & Tricks

Always Use -Raw When Reading JSON Files

# BAD - Reads as array of lines, breaks JSON structure
$json = Get-Content C:\data.json
$data = $json | ConvertFrom-Json  # ERROR!

# GOOD - Reads entire file as single string
$json = Get-Content C:\data.json -Raw
$data = $json | ConvertFrom-Json  # Works!

Access Properties with Bracket Notation for Special Characters

$json = '{"user-name":"jdoe","first.name":"John"}'
$data = $json | ConvertFrom-Json

# Dot notation doesn't work with hyphens or periods
# $data.user-name    # ERROR!
# $data.first.name   # Wrong property!

# Use bracket notation instead
$data.'user-name'    # "jdoe"
$data.'first.name'   # "John"

Test JSON Validity Before Converting

# Test if string is valid JSON
try {
    $null = $jsonString | ConvertFrom-Json -ErrorAction Stop
    Write-Output "Valid JSON"
} catch {
    Write-Output "Invalid JSON: $($_.Exception.Message)"
}

ConvertFrom-Json Creates PSCustomObjects, Not Hashtables

$json = '{"name":"Raymond","age":30}'
$obj = $json | ConvertFrom-Json

# This is a PSCustomObject, not a hashtable
$obj.GetType().Name  # PSCustomObject

# Can't use hashtable syntax
# $obj["name"]  # Won't work!

# Use dot notation instead
$obj.name  # "Raymond" - Works!

# To convert to hashtable:
$hash = @{}
$obj.PSObject.Properties | ForEach-Object {
    $hash[$_.Name] = $_.Value
}

Watch for Null Values in JSON

$json = '{"name":"Raymond","email":null}'
$data = $json | ConvertFrom-Json

# null in JSON becomes $null in PowerShell
$data.email -eq $null  # $true

# Check for null before using
if ($null -ne $data.email) {
    Send-Email -To $data.email
}

Deep Nesting Requires Increased -Depth

# Default depth is 2 - data beyond that gets stringified
$deep = @{
    L1 = @{ L2 = @{ L3 = @{ L4 = "value" } } }
}

# BAD - L3 and beyond become "[Object]" string
$json = $deep | ConvertTo-Json

# GOOD - Increase depth
$json = $deep | ConvertTo-Json -Depth 5
$restored = $json | ConvertFrom-Json -Depth 5
$restored.L1.L2.L3.L4  # "value"

Additional Resources