One of the more frustrating PowerShell warnings you’ll eventually encounter is:
WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 2.
At first glance, the solution seems simple: increase the -Depth parameter.
$obj | ConvertTo-Json -Depth 10
But how deep does your object really go?
If you’re working with dynamic data from APIs such as Microsoft Graph, Azure, REST APIs, or custom applications, hardcoding a depth value is often little more than a guess. Set it too low and data gets truncated. Set it unnecessarily high and your code becomes harder to maintain.
A better approach is to calculate the actual depth of the object before serializing it.
The Problem with ConvertTo-Json
By default, PowerShell only serializes nested objects to a depth of 2.
Consider the following JSON:
{
"user": {
"profile": {
"address": {
"city": "Amersfoort"
}
}
}
}
After converting it into a PowerShell object:
$obj = $json | ConvertFrom-Json
Using the default serialization:
$obj | ConvertTo-Json
Produces a warning and incomplete output because the object hierarchy exceeds the default depth.
This becomes particularly problematic when:
- Consuming APIs
- Storing API responses
- Building automation workflows
- Creating backup/export functionality
- Working with Microsoft Graph responses
Introducing Get-JsonDepth
To solve this problem, I created the Get-JsonDepth function.
The function recursively traverses:
- PSCustomObjects
- Hashtables
- Dictionaries
- Arrays and collections
And returns the maximum nesting depth it encounters.
Example:
$depth = Get-JsonDepth -InputObject $obj
$depth
Output:
5
You can then safely use that value during serialization:
$obj | ConvertTo-Json -Depth $depth
No guessing required.
The Function
function Get-JsonDepth {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[AllowNull()]
[object]$InputObject
)
begin {
function Get-DepthInternal {
param(
[AllowNull()]
[object]$Object,
[int]$CurrentDepth = 1
)
if ($null -eq $Object) {
return $CurrentDepth
}
if (
$Object -is [string] -or
$Object.GetType().IsPrimitive -or
$Object -is [datetime] -or
$Object -is [decimal] -or
$Object -is [guid]
) {
return $CurrentDepth
}
$childValues = @()
if ($Object -is [System.Collections.IDictionary]) {
$childValues = $Object.Values
} elseif ($Object -is [System.Collections.IEnumerable] -and $Object -isnot [string]) {
$childValues = @($Object)
} elseif ($Object -is [psobject]) {
$childValues = @(
$Object.PSObject.Properties |
ForEach-Object { $_.Value }
)
} else {
return $CurrentDepth
}
if ($childValues.Count -eq 0) {
return $CurrentDepth
}
$maxDepth = $CurrentDepth
foreach ($child in $childValues) {
$childDepth = Get-DepthInternal -Object $child -CurrentDepth $($CurrentDepth + 1)
$maxDepth = [Math]::Max($maxDepth, $childDepth)
}
return $maxDepth
}
}
process {
Get-DepthInternal -Object $InputObject -CurrentDepth 1
}
}
How It Works
The function evaluates the object type and inspects its children.
Dictionaries
if ($Object -is [System.Collections.IDictionary]) {
$childValues = $Object.Values
}
Collections
elseif ($Object -is [System.Collections.IEnumerable] -and $Object -isnot [string]) {
$childValues = @($Object)
}
PSCustomObjects
elseif ($Object -is [psobject]) {
$childValues = @(
$Object.PSObject.Properties |
ForEach-Object { $_.Value }
)
}
For each child, the function calls itself recursively while incrementing the depth counter.
$childDepth = Get-DepthInternal -Object $child -CurrentDepth $($CurrentDepth + 1)
The highest depth discovered becomes the result.
Real-World Example
Imagine you’re consuming Microsoft Graph:
$response = Invoke-RestMethod -Uri $Uri -Headers $Headers
$response | ConvertTo-Json
PowerShell may warn about truncation:
Resulting JSON is truncated as serialization has exceeded the set depth of 2.
Instead:
$depth = Get-JsonDepth $response
$response | ConvertTo-Json -Depth $depth
The exported JSON now accurately reflects the complete object hierarchy.
Why Not Just Use -Depth 100?
I’ve seen this recommendation countless times:
$obj | ConvertTo-Json -Depth 100
While it generally works, it has a few drawbacks.
1. Avoid Magic Numbers
Using a hardcoded value such as 100 raises immediate questions:
- Why 100?
- Why not 20, 50, or 500?
- What assumption is this based on?
Without additional context, future maintainers have no way of knowing whether the value was carefully chosen or simply guessed.
2. Consider Performance
Traversing deeply nested object graphs can become increasingly expensive as recursion depth grows. By using a realistic maximum depth, you limit unnecessary processing, reduce resource consumption, and make the function’s behavior more predictable.
3. Self-Documenting Code
Compare:
$obj | ConvertTo-Json -Depth 100
With:
$depth = Get-JsonDepth $obj
$obj | ConvertTo-Json -Depth $depth
The second version immediately communicates intent.
Example Usage
$json = Get-Content .\input.json -Raw
$obj = $json | ConvertFrom-Json
$depth = Get-JsonDepth -InputObject $obj
Write-Host "Object depth: $depth"
$obj | ConvertTo-Json -Depth $depth
Potential Improvements
Depending on your use case, you could extend the function to:
- Return both minimum and maximum depth
- Display the deepest property path
- Exclude specific property names
- Detect circular references
- Produce depth statistics for complex API responses
These enhancements can be useful when debugging large automation workflows or Graph API responses.
Conclusion
PowerShell’s JSON serialization is excellent, but the default depth of 2 often catches administrators and developers off guard when working with modern APIs and complex object structures.
Rather than guessing a depth value or defaulting to -Depth 100, consider calculating the required depth dynamically.
By determining the maximum nesting level of your object before serialization, you can:
- Eliminate truncation warnings
- Avoid arbitrary depth values
- Improve maintainability
- Make your scripts self-documenting
- Produce more predictable JSON output
Get-JsonDepth is a small utility function, but it can remove a surprising amount of frustration when working with Microsoft Graph, Azure, REST APIs, and deeply nested JSON payloads.

Leave a Reply