When loading an object from a json file one can normally set the value on properties and write the file back out like so:
$manifest = (gc $manifestPath) | ConvertFrom-Json -AsHashtable
$manifest.name = "$($manifest.name)-sxs"
$manifest | ConvertTo-Json -depth 100 | Out-File $manifestPath -Encoding utf8NoBOM
But if the source json file contains comments, the object's properties can't be set:
// *******************************************************
// GENERATED FILE - DO NOT EDIT DIRECTLY
// *******************************************************
{
"name": "PublishBuildArtifacts"
}
Running the code above throws an error:
$manifest
id : 1D341BB0-2106-458C-8422-D00BCEA6512A
name : PublishBuildArtifacts
friendlyName : ms-resource:loc.friendlyName
description : ms-resource:loc.description
category : Build
visibility : {Build}
author : Microsoft Corporation
version : @{Major=0; Minor=1; Patch=71}
demands : {}
inputs : {@{name=CopyRoot; type=filePath; label=ms-resource:loc.input.label.CopyRoot; defaultValue=;
required=False; helpMarkDown=Root folder to apply copy patterns to. Empty is the root of the
repo.}, @{name=Contents; type=multiLine; label=ms-resource:loc.input.label.Contents;
defaultValue=; required=True; helpMarkDown=File or folder paths to include as part of the
artifact.}, @{name=ArtifactName; type=string; label=ms-resource:loc.input.label.ArtifactName;
defaultValue=; required=True; helpMarkDown=The name of the artifact to create.},
@{name=ArtifactType; type=pickList; label=ms-resource:loc.input.label.ArtifactType;
defaultValue=; required=True; helpMarkDown=The name of the artifact to create.; options=}…}
instanceNameFormat : Publish Artifact: $(ArtifactName)
execution : @{PowerShell=; Node=}
$manifest.name
PublishBuildArtifacts
$manifest.name = "sxs"
InvalidOperation: The property 'name' cannot be found on this object. Verify that the property exists and can be set.
When I strip the comments, I can overwrite the property.
Is there a way I can coax PowerShell to ignore the comments while loading the json file/convert the object and generate a writable object?
I'm not sure if this is intended, but seems like ConvertFrom-Json
is treating the comments on the Json as $null
when converting it to an object. This only happens if it's receiving an object[]
from pipeline, with a string or multi-line string it works fine.
A simple way to demonstrate this using the exact same Json posted in the question:
$contentAsArray = Get-Content test.json | Where-Object {
-not $_.StartsWith('/')
} | ConvertFrom-Json -AsHashtable
$contentAsArray['name'] = 'hello' # works
Here you can see the differences and the workaround, it is definitely recommended to use -Raw
on Get-Content
so you're passing a multi-line string to ConvertFrom-Json
:
$contentAsString = Get-Content test.json -Raw | ConvertFrom-Json -AsHashtable
$contentAsArray = Get-Content test.json | ConvertFrom-Json -AsHashtable
$contentAsString.PSObject, $contentAsArray.PSObject | Select-Object TypeNames, BaseObject
TypeNames BaseObject
--------- ----------
{System.Collections.Hashtable, System.Object} {name}
{System.Object[], System.Array, System.Object} {$null, System.Collections.Hashtable}
$contentAsArray['name'] # null
$null -eq $contentAsArray[0] # True
$contentAsArray[1]['name'] # PublishBuildArtifacts
$contentAsArray[1]['name'] = 'hello'
$contentAsArray[1]['name'] # hello