convertto-json — Serialise PowerShell objects to JSON strings across all 5 shells
Equivalents in every shell
jq -n --arg name alice '{name: $name}'`jq -n` (null input) is the canonical way to build a JSON object from shell variables — `--arg key value` for strings, `--argjson key value` for numbers / objects / arrays parsed as JSON. For an array of objects, switch to a heredoc + `jq -s` or generate inline via `printf` and pipe through `jq .`.
jq -n --arg name alice '{name: $name}'Same external `jq`. macOS has no jq in base — install via `brew install jq`. Zsh's `print -r --` is useful when feeding multi-line literal JSON into `jq` without backslash-escaping every internal quote.
jq -n --arg name alice '{name: $name}'Same external `jq`. Fish's string-quoting story is simpler — `jq -n --arg name "$value" '{name: $name}'` works even when `$value` contains spaces or special characters without any escape gymnastics.
@{name='alice'} | ConvertTo-JsonTakes any object or hashtable on the pipeline (or via `-InputObject`) and emits a pretty-printed JSON string. Aliases: none. Numbers, strings, booleans, `$null`, dates (as ISO 8601), and nested hashtables / arrays all serialise correctly. Use `-Compress` for one-line output.
powershell -NoProfile -Command "@{name='alice'} | ConvertTo-Json -Compress"cmd.exe has no native object model. Shell out to PowerShell via `powershell -NoProfile -Command` (skips $PROFILE for faster startup) and rely on its `ConvertTo-Json`. For pure-cmd JSON construction, the only realistic option is `echo {"name":"alice"}` with manual quote-escaping — error-prone past one field.
Worked examples
Build a nested JSON object from a hashtable
jq -n --arg name alice --argjson age 30 '{user: {name: $name, age: $age}}'@{user=@{name='alice'; age=30}} | ConvertTo-JsonSerialise an array of objects (deep tree) and stream to disk
jq -n '[{id:1, tags:["a","b"]}, {id:2, tags:["c"]}]' > out.json@(@{id=1; tags=@('a','b')}, @{id=2; tags=@('c')}) | ConvertTo-Json -Depth 10 | Out-File out.json -Encoding utf8Emit compact one-line JSON for HTTP body payloads
jq -nc --arg name alice '{name: $name}'@{name='alice'} | ConvertTo-Json -CompressGotchas
- **The #1 footgun**: `ConvertTo-Json` defaults to `-Depth 2` and SILENTLY TRUNCATES anything deeper — your nested objects become string representations like `"System.Collections.Hashtable"`. Always pass `-Depth 10` (or higher) for non-trivial graphs. pwsh 7.3+ warns on truncation; 5.1 + early 7.x stays silent.
- Dates serialise as ISO 8601 strings (`2026-05-16T07:30:00`) WITHOUT timezone offset — the local kind is dropped. To preserve timezone, use `[DateTimeOffset]::Now.ToString("o")` explicitly before serialising. For Unix timestamps, convert to `[DateTimeOffset]::UtcNow.ToUnixTimeSeconds()` first.
- `$null` serialises to JSON `null` (correct), but UNDEFINED PROPERTIES on a `[PSCustomObject]` are silently OMITTED from the output — different from JavaScript `JSON.stringify` which preserves them as `null`. To force-include, set them explicitly: `$obj.foo = $null`.
- Hashtable key ORDER is not preserved on `[hashtable]` (it's an unordered map). Use `[ordered]@{a=1; b=2}` to force insertion order in the output JSON. pwsh 5.1 always reorders; 7+ is more stable but still not guaranteed.
- Enums serialise as their NUMERIC value (`0`, `1`, …) not their name string. To get the readable name, cast `.ToString()` before piping. PSCustomObjects with `[Flags]` enums round-trip lossily.