使用PowerShell中的注释转换为JSON

使用PowerShell中的注释转换为JSON,json,powershell,Json,Powershell,我有一个非常简单的json,这段代码适合他: function Get-CustomHeaders() { return Get-Content -Raw -Path $JsonName | ConvertFrom-Json } 但是,如果我的json有任何注释//wololo,它就会中断。让此解析器接受注释是否太难?在转换之前从输入中删除注释行: (Get-Content $JsonName) -replace '^\s*//.*' | Out-String | ConvertFrom

我有一个非常简单的json,这段代码适合他:

function Get-CustomHeaders() {
   return Get-Content -Raw -Path $JsonName | ConvertFrom-Json
}

但是,如果我的json有任何注释
//wololo
,它就会中断。让此解析器接受注释是否太难?

在转换之前从输入中删除注释行:

(Get-Content $JsonName) -replace '^\s*//.*' | Out-String | ConvertFrom-Json

另一个答案中的解决方案仅删除位于行首(带空格或不带空格)的
//comments
,而不删除
/*多行注释*/

此代码删除所有类型的
/
/*多行注释*/
/

$configFile = (Get-Content path-to-jsonc-file -raw)
# Keep reading, for an improvement
# $configFile = $configFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/'
正如@JiříHerník在他的回答中所指出的,这个表达式没有考虑到字符串中包含注释的情况,例如
“url”:http://mydomian.com“
。要处理此案件:

$configFile = $configFile -replace '(?m)(?<=^([^"]|"[^"]*")*)//.*' -replace '(?ms)/\*.*?\*/'
结果:

{

  "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"
        }
      }
    ]
  }
}

这里有一个无法用前面的答案正确处理的示例:

{
"url":"http://something" // note the double slash in URL
}
这里是regexp,它也解决了这个问题

$configFile = $configFile -replace '(?m)(?<=^([^"]|"[^"]*")*)//.*' -replace '(?ms)/\*.*?\*/'

$configFile=$configFile-replace'(?m)(?捕获字符串、转义符和注释的所有组合的更简单模式是:

$configFile = $configFile -replace '("(\\.|[^\\"])*")|/\*[\S\s]*?\*/|//.*', '$1';
这假设文件有效,没有未关闭的字符串或注释。如果出现此问题,则无效文件超出范围

第一部分
(“(\\.\\\\\\\\\\”)*”
匹配完整字符串并跳过任何转义字符,包括
\\
\“
。此部分被捕获,以便可以将其放回替换字符串中

第二部分
/\*[\S\S]*?\*/
匹配多行注释。它使用
[\S\S]
而不是
,因此换行符也匹配。它是非空白字符(
\S
)和空白字符(
\S
)的组合.
*?
是一个惰性的重复,因此它希望尽可能少地匹配,因此它不会跳过任何结束
*/

最后一部分
/.*
匹配单行注释。
不会匹配任何换行符,因此它只会匹配到行尾


匹配字符串时,将其捕获到插槽1中。匹配注释时,将不捕获任何内容。替换为插槽1中的任何内容(
$1
)。结果是字符串被匹配但保留,但注释被删除。

我编写了一个函数,该函数接收任何注释,如果找到,将它们放回JSON文件中

这还允许读取和写入JSON文件

在v5.1和v7中测试的.Tested中有注释

# 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
#辅助函数
#将参数内容的内容写入文件。
#如果文件不存在,将创建该文件。
函数写入文件{
Param([Parameter(Mandatory=$true,Position=0)][string]$path,[Parameter(Mandatory=$true,Position=1)][string]$content)
[System.IO.File]::writealText($path,$content)
}
函数调用ReadWriteJSON{
调用ReadWriteJSON-filePath$jsonFilePath“Write”“Prop 1”“进入您的机器!”
.示例(从文件中读取属性)
PS>Invoke ReadWriteJSON-filePath$jsonFilePath“Read”“Prop 2”
PS>温度
#>
Param
(
[参数(必需=$true,HelpMessage='JSON文件的文件路径')]
[String]$filePath,
[参数(必需=$true,HelpMessage='此参数为读或写。')]
[字符串]$Mode,
[参数(必需=$true,HelpMessage='JSON对象的属性')]
[String]$Property,
[参数(必需=$false,HelpMessage='JSON对象的新属性')]
[字符串]$newValue
)
#如果存在文件,则设置其内容,否则将内容变量设置为空。
if(测试路径-LiteralPath$filePath){
$contents=Get Content-LiteralPath$filePath
$contents=$contents-replace'\s*'#替换任何空格,以便检查长度。
}
否则{
$contents=''
}
#如果文件不存在或内容为空
if((测试路径-LiteralPath$filePath)-eq$false-或$contents.length-eq 0){
写入文件$filePath$jsonSettingFileDefaultContents
}
#这将允许json文件中有单行和多行注释。
#用于删除注释的正则表达式:https://stackoverflow.com/a/59264162/8262102

$jsonContents=(Get Content-LiteralPath$filePath-Raw)-replace'(?m)(?JSON(根据定义,规范)不支持注释。这是真的,我很欣赏这些信息。但是对于这种情况,我非常需要注释。您知道它在我们的世界中是如何工作的=p当您需要弄清楚如何解析它们时:
-join(Get Content-Path$JsonName)-replace'^//.'| ConvertFrom Json
powershell项目中存在添加jsonc支持的问题:如@sschoof的SO注释中的上述github问题所述,pwsh现在确实支持注释。对于多行/*…*/样式的注释,可以使用(Get Content$JsonName)-replace'(?ms)/**/'-replace'/.*/'@stimms您需要将文件作为一个整体来读取,并转义不希望被视为通配符的星号:
(gc$jsonname-raw)-replace'(?ms)/\*.*.*/'.
您还需要忽略双引号中的//,例如在URL配置值的情况下。(获取内容'appsettings.json'-raw)-替换“(?此外,PowerShell Core 6支持Json文件中的即时注释这也将在这里的“serverUrl”:“//localhost:5341”中捕获两个斜杠,您无法看到它,因为它位于示例中其他注释块的内部。
# 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