I have a very simple json and this code works for him:
function Get-CustomHeaders() {
return Get-Content -Raw -Path $JsonName | ConvertFrom-Json
}
However, if my json has any comments // wololo
it breaks. Would it be too hard to make this parser accept comments ?
在转换之前从您的输入中删除注释行:
(Get-Content $JsonName) -replace '^\s*//.*' | Out-String | ConvertFrom-Json
The solution in the other answer only removes // comments
if they are at the beginning of a line (with or without spaces), and doesn't remove /* multiline comments */
This code removes all kind of //
and /* multiline comments */
/
$configFile = (Get-Content path-to-jsonc-file -raw)
# Keep reading, for an improvement
# $configFile = $configFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/'
As @Jiří Herník indicates in his answer, this expression doesn't have into account the case of strings with comments inside it, for example "url": "http://mydomian.com"
. To handle this case:
$configFile = $configFile -replace '(?m)(?<=^([^"]|"[^"]*")*)//.*' -replace '(?ms)/\*.*?\*/'
for example removing the comments in this file:
{
// https://github.com/serilog/serilog-settings-configuration
"Serilog": {
"MinimumLevel": "Error", // Verbose, Debug, Information, Warning, Error or Fatal
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "D:\\temp\\MyService\\log.txt",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] ({App}) ({Environment}) {Message:lj}{NewLine}{Exception}"
}
},
{/*
"Name": "Seq",*/
"Args": {
"serverUrl": "http://localhost:5341"
}
}
]
}
}
results in:
{
"Serilog": {
"MinimumLevel": "Error",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "D:\\temp\\MyService\\log.txt",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] ({App}) ({Environment}) {Message:lj}{NewLine}{Exception}"
}
} ,
{
"Args": {
"serverUrl": "http://localhost:5341"
}
}
]
}
}
Here you have an example which can't be handled right by previous answers:
{
"url":"http://something" // note the double slash in URL
}
so here is regexp that solves also this problem.
$configFile = $configFile -replace '(?m)(?<=^([^"]|"[^"]*")*)//.*' -replace '(?ms)/\*.*?\*/'
IMPORTANT NOTE:
Powershell 6.0+ can load JSON with comments in it.
A simpler pattern that catches all combinations of string, escapes and comments is:
$configFile = $configFile -replace '("(\\.|[^\\"])*")|/\*[\S\s]*?\*/|//.*', '$1';
This assumes the file is valid, with no unclosed strings or comments. Invalid files are beyond the scope if this question.
The first part ("(\\\\.|[^\\\\"])*")
matches full strings and skips any escaped characters, including \\\\
and \\"
. This is captured so it can be placed back in the replacement string.
The second part /\\*[\\S\\s]*?\\*/
matches multiline comments. It uses [\\S\\s]
instead of .
, so linebreaks are also matched. It is a combination of non-whitespace characters ( \\S
) and whitespace characters ( \\s
). The *?
is a lazy repetition, so it will prefer to match as little as possible, so it won't skip over any closing */
.
The last part //.*
matches single line comments. The .
won't match any linebreak, so it will only match until the end of the line.
When a string is matched, it is captured into slot 1. When a comment is matched, nothing is captured. The replacement is with whatever is in slot 1 ( $1
). The result is that strings are matched but preserved, but comments are removed.
I wrote a function that takes any comments and puts them back into the JSON file if found.
This also allows reading and writing to the JSON file.
There are comments within. Tested in v5.1 and v7.
# Helper Function
# Write the contents of argument content to a file.
# Will create the file if it does not exist.
Function Write-ToFile {
Param ([Parameter(Mandatory=$true, Position=0)] [string] $path,[Parameter(Mandatory=$true, Position=1)] [string] $content)
[System.IO.File]::WriteAllText($path, $content)
}
Function Invoke-ReadWriteJSON {
<#
.SYNOPSIS
Reads and writes properties from a JSON file.
.DESCRIPTION
This will allow JSON files to have comments, either multi-line or single line
comments are supported.
If the file does not exist or is empty then the default file contents are
written to it.
.NOTES
Author: Ste
Date Created: 2021.05.01
Tested with PowerShell 5.1 and 7.1.
Posted here: https://stackoverflow.com/questions/51066978/convert-to-json-with-comments-from-powershell
.BUGS: NA
.TODO: NA
.PARAMETER filePath
The file path of the JSON file.
.PARAMETER Mode
This parameter is either Read or Write.
.PARAMETER Property
The property of the JSON object.
.PARAMETER newValue
The new property of the JSON object.
.INPUTS
None. You cannot pipe objects to Add-Extension.
.OUTPUTS
Writes to or reads a file using the filePath parameter.
.EXAMPLE (Write the property "Prop 1" with the value "Get in you machine!" to a file)
PS> Invoke-ReadWriteJSON -filePath $jsonFilePath "Write" "Prop 1" "Get in you machine!"
.EXAMPLE (Read a property from a file)
PS> Invoke-ReadWriteJSON -filePath $jsonFilePath "Read" "Prop 2"
PS> temp
#>
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The file path of the JSON file.')]
[String]$filePath,
[Parameter(Mandatory = $true, HelpMessage = 'This parameter is either Read or Write.')]
[String]$Mode,
[Parameter(Mandatory = $true, HelpMessage = 'The property of the JSON object.')]
[String]$Property,
[Parameter(Mandatory = $false, HelpMessage = 'The new property of the JSON object.')]
[String]$newValue
)
# If there is a file then set its content else set the content variable to empty.
if (Test-Path -LiteralPath $filePath) {
$contents = Get-Content -LiteralPath $filePath
$contents = $contents -replace '\s*' # Replace any whitespaces so that the length can be checked.
}
else {
$contents = ''
}
# if the file does not exist or the contents are empty
if ((Test-Path -LiteralPath $filePath) -eq $false -or $contents.length -eq 0) {
Write-ToFile $filePath $jsonSettingFileDefaultContents
}
# This will allow single and multiline comments in the json file.
# Regex for removing comments: https://stackoverflow.com/a/59264162/8262102
$jsonContents = (Get-Content -LiteralPath $filePath -Raw) -replace '(?m)(?<=^([^"]|"[^"]*")*)//.*' -replace '(?ms)/\*.*?\*/' | Out-String | ConvertFrom-Json
# Grab the comments that will be used late on.
$jsonComments = (Get-Content -LiteralPath $filePath -Raw) -replace '(?s)\s*\{.*\}\s*'
# Read the property.
if ($Mode -eq "Read") {return $jsonContents.$Property}
# Write the property.
if ($Mode -eq "Write") {
$jsonContents.$Property = $newValue
$jsonContents | ConvertTo-Json -depth 32 | set-content $filePath
# Trims any whitespace from the beginning and end of contents.
Set-content $filePath ((Get-Content -LiteralPath $filePath -Raw) -replace '(?s)^\s*|\s*$')
}
# If there are comments then this section will add them back in. Important to
# read contents with -Raw switch here.
if ($jsonComments.length -gt 0) {
$jsonNewcontents = (Get-Content -LiteralPath $filePath -Raw) -replace '(?m)(?<=^([^"]|"[^"]*")*)//.*' -replace '(?ms)/\*.*?\*/'
# Trims any whitespace from the beginning and end of contents.
Set-content $filePath (("$jsonComments`n" + $jsonNewcontents) -replace '(?s)^\s*|\s*$')
}
}
$deskTopFolder = [Environment]::GetFolderPath("DesktopDirectory")
$jsonFilePath = "$deskTopFolder\color-dialog-settings.json"
$jsonSettingFileDefaultContents = @'
// Some comments go here.
// Some comments go here.
// Some comments go here.
{
"Prop 1": "temp",
"Prop 2": "temp"
}
'@
# Write the JSON property.
# Invoke-ReadWriteJSON -filePath $jsonFilePath "Write" "Prop 1" "Get in you machine!"
# Read the JSON property.
Invoke-ReadWriteJSON -filePath $jsonFilePath "Read" "Prop 2"
# PS> temp
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.