See below both $a
and $s
are strings containing the text "String"
but each serializes differently with ConvertTo-JSON.
Why won't $s | ConvertToJson
produce "String"
??
PS W:\PowerShell\powowshell> $a="String"
PS W:\PowerShell\powowshell> $a
String
PS W:\PowerShell\powowshell> $a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS W:\PowerShell\powowshell> $a | ConvertTo-Json
"String"
PS W:\PowerShell\powowshell> $s
String
PS W:\PowerShell\powowshell> $s.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS W:\PowerShell\powowshell> $s | ConvertTo-Json
{
"value": "String",
"required": "true"
}
$s
is the parameterValue
of a .ps1
inspected with Get-Help
:
PS W:\PowerShell\powowshell> $cmd = (get-help -full W:\PowerShell\powowshell\examples\components\dosdir.ps1).Syntax.syntaxItem[0].parameter
PS W:\PowerShell\powowshell> $cmd | convertto-json
{
"description": [
{
"Text": "The path to the directory to be listed"
}
],
"parameterValue": {
"value": "String",
"required": "true"
},
...
$s = $cmd.parameterValue
dosdir.ps1:
param(
[String]$Path
)
CMD /C "DIR /B $Path"
PowerShell makes it easy to modify JSON by converting JSON to a PSCustomObject. The object can then be modified easily like any other object. The object can then be exported back out using ConvertTo-Json. Now if we're on a computer without PowerShell 7.1 we try to run the same command in PowerShell 5.1 but it fails!
The ConvertFrom-Json cmdlet converts a JavaScript Object Notation (JSON) formatted string to a custom PSCustomObject object that has a property for each field in the JSON string. JSON is commonly used by web sites to provide a textual representation of objects.
-Depth. Specifies how many levels of contained objects are included in the JSON representation. The value can be any number from 0 to 100 . The default value is 2 .
PowerShell's ETS (Extended Type System) allows you to decorate any object with additional properties (that are directly accessible only to PowerShell code).
If you do so with a [string]
instance (whether you do it yourself or another command does it for you[1]), these additional properties will surface when the object is serialized with ConvertTo-Json
:
# Add a .foo property with value 'bar' to a string.
$decoratedString = 'hi' | Add-Member -PassThru foo bar
# Output the string as-is.
# The added property does NOT show.
$decoratedString
'---'
# Serialize the string to JSON.
# The added property DOES show and the string's actual content
# is presented as pseudo-property .value
$decoratedString | ConvertTo-Json
The above yields:
hi
---
{
"value": "hi",
"foo": "bar"
}
This GitHub issue discusses this surprising behavior.
Workaround:
# .psobject.BaseObject returns the underlying, undecorated object.
PS> $decoratedString.psobject.BaseObject | ConvertTo-Json
hi
[1] As js2010 points out, the data-retrieving PowerShell provider cmdlets - Get-ChildItem
, Get-Item
, Get-Content
, ... - all add a fixed number of NoteProperty
members to the objects they output, namely PSPath
, PSParentPath
, PSChildName
, PSDrive
, PSProvider
.
Therefore, you'll run into the same problem detailed above if you serialize a string that was obtained with Get-Content
:
PS> 'hi' > t.txt; Get-Content t.txt | ConvertTo-Json
{
"value": "hi",
"PSPath": "/Users/jdoe/t.txt",
"PSParentPath": "/Users/jdoe",
"PSChildName": "t.txt",
"PSDrive": {
"CurrentLocation": "Users/jdoe",
"Name": "/",
"Provider": {
"ImplementingType": "Microsoft.PowerShell.Commands.FileSystemProvider",
"HelpFile": "System.Management.Automation.dll-Help.xml",
"Name": "FileSystem",
"PSSnapIn": "Microsoft.PowerShell.Core",
...
Note that in the case of a string these extra properties are lost when a new string is constructed, either by string concatenation or by applying a string operator such as -replace
:
# String concatenation
PS> 'hi' > t.txt; (Get-Content t.txt) + '!' | ConvertTo-Json
hi!
# Using -replace
PS> (Get-Content t.txt) -replace 'i', 'o' | ConvertTo-Json
ho
Also note that this per-output-object decorating adds quite a bit of memory and performance overhead; for Get-Content
, GitHub issue #7537 suggests offering an opt-out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With