Powershell 从输出中删除双引号中的键

huangapple go评论97阅读模式
英文:

Powershell Removing double quotes from Keys in output

问题

如何从PowerShell输出中删除双引号?

curl语句只有在删除键名周围的引号时才运行。我已经使用硬编码的JSON对象测试了我的端点。(见期望的输出)

curl语句在包含键名引号的PowerShell构建的对象中失败了。(见实际输出)

非常感谢任何提示。

实际输出

{"ISOYear":[{"ISOYear":2023}],"ISOWeek":[{"ISOWeek":23}],"SkillCategory":"Warehouse","Building":"ABC"}

期望的输出

{ISOYear:[{ISOYear:2023}],ISOWeek:[{ISOWeek:29}],SkillCategory:'Warehouse',Building:'ABC'}

PowerShell脚本

$hash=@{
ISOYear=@()
ISOWeek=@()
SkillCategory="Warehouse"
Building="ABC"
}
$hash.ISOYear += @{ISOYear=2023}
$hash.ISOWeek += @{ISOWeek=23}

New-Object -TypeName PSObject -Property $hash | Select-Object ISOYear,ISOWeek,SkillCategory,Building
Write-Output $hash

Pause

#这与我的CURL语句不兼容
#键包括"
$json = $hash | ConvertTo-Json -Compress
Write-Output $json 

#--->为了转义\",添加了这个 $json 
$jsonEscaped = $json  -replace '([\\]*)"', '$1$1\"'
Write-Output $jsonEscaped 

Pause

#这适用于我的CURL语句
#键不包括"
$CURL_DATA="{ISOYear:[{ISOYear:$O_YEAR}],ISOWeek:[{ISOWeek:$O_WEEK}],SkillCategory:'Warehouse',Building:'ABC'}";
Write-Output $CURL_DATA

Pause
#适用
#curl.exe -k --ntlm -u: -X POST --data $CURL_DATA https://localhost:44379/api/controller/method -H "Content-Type: application/json; charset=utf-8"

#已更新以接收"转义的json
#curl.exe -k --ntlm -u: -X POST --data "$jsonEscaped" https://localhost:44379/api/controller/method -H "Content-Type: application/json; charset=utf-8"

Pause

===================更新 1===================

我已经添加了使用PowerShell构建的curl语句。我刚刚发现我必须像"$json"这样调用它,它至少能够到达方法。现在出现以下错误

这是我现在收到的错误。
> {"errors":{"Building":["在解析值时遇到意外字符:B。路径'Building',第1行,位置
> 33。"]},"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"发生一个或多个验证错误
> 。","status":400,"traceId":"00-4cbdc390e1a314d50861365e4c34aa12-f1f7defa11ef159e-00"}

===================更新 2===================

根据@mklement0的建议,他的链接提供了一个有效的替换语句。

> 对于期望"转义的程序,请使用以下-replace操作来以健壮的方式进行额外的转义:

> '...' -replace '([\]*)"', '$1$1"'

所以我把这个加到了我的PowerShell脚本中。

#--->为了转义",添加了这个 $json
$jsonEscaped = $json -replace '([\]*)"', '$1$1"'
Write-Output $jsonEscaped

我已经更新了上面的完整脚本。

英文:

How can I remove the double quotes from PowerShell output?

The curl statement only runs when the quotes around key names are removed. I have tested my endpoint with a hard coded JSON object. (see Desired Output)

The curl statement fails with the PowerShell constructed object where the key name quotes are included. (see Actual Output)

Any hints are most appreciated.

Actual Output

{"ISOYear":[{"ISOYear":2023}],"ISOWeek":[{"ISOWeek":23}],"SkillCategory":"Warehouse","Building":"ABC"}

Desired Output

{ISOYear:[{ISOYear:2023}],ISOWeek:[{ISOWeek:29}],SkillCategory:'Warehouse',Building:'ABC'}

PowerShell Script

  $hash=@{
        ISOYear=@()
        ISOWeek=@()
        SkillCategory="Warehouse"
        Building="ABC"
        }
    $hash.ISOYear +=  @{ISOYear=2023}
    $hash.ISOWeek +=  @{ISOWeek=23}
    
    New-Object -TypeName PSObject -Property $hash | Select-Object ISOYear,ISOWeek,SkillCategory,Building
    Write-Output $hash
    
    Pause
    
    #THIS DOES NOT WORK WITH MY CURL STATEMENT
    #KEYs include "
    $json = $hash | ConvertTo-Json -Compress
    Write-Output $json 
    
    #--->ADDED THIS TO ESCAPE \" $json 
    $jsonEscaped = $json  -replace '([\\]*)"', '$1$1\"'
    Write-Output $jsonEscaped 

    Pause
    
    #THIS WORKS WITH MY CURL STATEMENT
    #KEYs do not include "
    $CURL_DATA="{ISOYear:[{ISOYear:$O_YEAR}],ISOWeek:[{ISOWeek:$O_WEEK}],SkillCategory:'Warehouse',Building:'ABC'}";
    Write-Output $CURL_DATA
    
    Pause
    #WORKS
    #curl.exe -k --ntlm -u: -X POST --data $CURL_DATA https://localhost:44379/api/controller/method -H "Content-Type: application/json; charset=utf-8"

#UPDATED TO RECEIVE \" escaped json
#curl.exe -k --ntlm -u: -X POST --data "$jsonEscaped" https://localhost:44379/api/controller/method -H "Content-Type: application/json; charset=utf-8"
    
    Pause

===================UPDATE 1===================

I have added the curl statement using the PowerShell constructed object. I found a few minutes ago that I have to call it like "$json" and it at least gets to the method. Here's the error now

This is the error I receive now.
> {"errors":{"Building":["Unexpected
> character encountered while parsing value: B. Path 'Building', line 1,
> position
> 33."]},"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One
> or more validation errors
> occurred.","status":400,"traceId":"00-4cbdc390e1a314d50861365e4c34aa12-f1f7defa11ef159e-00"}

===================UPDATE 2===================

Per @mklement0, his link gave a replace statement which works.

> For programs that expect "-escaping, use the following -replace
> operation to robustly perform the extra escaping programmatically:
>
> '...' -replace '([\]*)"', '$1$1"'

So I added this to my PowerShell script.

#--->ADDED THIS TO ESCAPE \" $json 
$jsonEscaped = $json  -replace '([\\]*)"', '$1$1\"'
Write-Output $jsonEscaped 

I have updated the full script above.

答案1

得分: 1

以下是翻译的内容:

"Your JSON string - with properly quoted property names - does work, but a long-standing bug prevented it from getting passed properly to curl.exe:

  • As detailed in this answer, Windows PowerShell and PowerShell (Core) up to v7.2.x require " characters embedded in PowerShell strings to be explicitly escaped as \" when passed to external programs such as curl.exe

  • Passing a programmatically escaped value, ($json -replace '(\\*)"', '$1$1\"'), in lieu of just $json to curl.exe solved the problem.

    • For instance, this programmatic escaping turns verbatim { &quot;foo&quot;: &quot;3\&quot; of snow&quot; } into verbatim { \&quot;foo\&quot;: \&quot;3\\\&quot; of snow\&quot; }, which PowerShell then encloses in &quot;...&quot; on the process command line.<sup>[1]</sup>

**一些 API 接受一种_简化的_ JSON 格式,不符合标准,在这种格式中,属性名称可能是_未加引号_的,字符串值可以用 &#39;...&#39; 而不是 &quot;...&quot; 包围。

考虑到您的网络服务似乎接受这种简化格式,使用它确实可以避免 &quot; 相关的 bug(假设没有包含嵌入 &quot; 的属性_值_)。

虽然 ConvertFrom-Json 也可以_读取_这种简化格式(在两个 PowerShell 版本中都可以),但 ConvertTo-Json 不能_创建_它(在两个版本中都不能)。

以下是一个自定义实现,用于创建此格式,ConvertTo-SimplifiedJson;假设它已经定义,以下是示例:

&#39;{&quot;ISOYear&quot;:[{&quot;ISOYear&quot;:2023}],&quot;ISOWeek&quot;:[{&quot;ISOWeek&quot;:23}],&quot;SkillCategory&quot;:&quot;Warehouse&quot;,&quot;Building&quot;:&quot;ABC&quot;}&#39; |
  ConvertFrom-Json |
  ConvertTo-SimplifiedJson

输出:

{ISOYear:[{ISOYear:2023}],ISOWeek:[{ISOWeek:23}],SkillCategory:&#39;Warehouse&#39;,Building:&#39;ABC&#39;}

ConvertTo-SimplifiedJson 源代码如下:

# 为给定对象(图形)创建简化的 JSON 表示形式,使用未加引号的属性名称(如果可能),以及字符串值的单引号括起来。
# 注意:
#  * 结果表示形式已压缩(没有空格以提高可读性)。
#  * 不能在 JSON 中表示的属性值将表示为字符串:
#    * [datetime] 和 [datetimeoffset] 实例将被表示为 ISO 8601 时间戳的字符串,使用 .ToString(&#39;o&#39;)
#    * 所有其他此类类型都将表示为其 .ToString() 值。
function ConvertTo-SimplifiedJson {
  param([Parameter(ValueFromPipeline)] [object] $InputObject)
  begin {
    function test-ListLike { 
      param($o) 
      $o -is [System.Collections.IEnumerable] -and $o -isnot [string] -and $o -isnot [System.Collections.IDictionary] 
    }
  }
  process {
    $properties = 
      if ($InputObject -is [System.Collections.IDictionary]) {
        $InputObject.GetEnumerator()
      } else {
        $InputObject.psobject.Properties
      }
    if (test-ListLike $InputObject) {
      # 数组作为单个输入对象 -&gt; 递归
      &#39;[&#39; + $(foreach ($o in $InputObject) { ConvertTo-SimplifiedJson $o }) + &#39;]&#39;
    }
    elseif ($InputObject.GetType() -in [datetime], [datetimeoffset]) {
      &quot;&#39;{0}&#39;&quot; -f $InputObject.ToString(&#39;o&#39;) # 使用 ISO 8601 时间字符串
    }
    elseif ($InputObject -is [bool]) {
      &quot;$($InputObject)&quot;.ToLower() # JSON 要求大小写完全匹配的 &#39;true&#39; 或 &#39;false&#39;
    }
    elseif ($InputObject.GetType() -in [byte], [sbyte], [int16], [uint16], [int32], [uint32], [int64], [uint64], [bigint], [decimal], [float], [double] ) {
      # 数值类型。
      [string] $InputObject # 使用与文化无关的字符串化,在 JSON 中不加引号嵌入。
    }
    elseif ($InputObject.GetType() -in [char], [string] -or $properties.Count -eq 0) {
      # 字符、字符串或不支持的没有属性的对象。
      &quot;&#39;{0}&#39;&quot; -f ($InputObject -replace &quot;&#39;&quot;, &quot;\&#39;&quot;)
    }
    else { 
      # 不能映射到 JSON 基元的(嵌套)对象。
      # 递归处理其属性。
      $sep = &#39;{&#39;
      -join $(
        foreach ($p in $properties) {
          $name = if ($p.Name -notmatch &#39;\W&#39;) { $p.Name } else { &quot;&#39;{0}&#39;&quot; -f ($p.Name -replace &quot;&#39;&quot;, &quot;\&#39;&quot;) }
          $value = ConvertTo-SimplifiedJson $p.Value
          &#39;{0}{1}:{2}&#39; -f $sep, $name, $value
          $sep = &#39;,&#39;
        }
      ) + &#39;}&#39;
    }
  }
}

<sup>[1] 换

英文:

Your JSON string - with properly quoted property names - does work, but a long-standing bug prevented it from getting passed properly to curl.exe:

  • As detailed in this answer, Windows PowerShell and PowerShell (Core) up to v7.2.x require &quot; characters embedded in PowerShell strings to be explicitly escaped as \&quot; when passed to external programs such as curl.exe

  • Passing a programmatically escaped value, ($json -replace &#39;(\\*)&quot;&#39;, &#39;$1$1\&quot;&#39;), in lieu of just $json to curl.exe solved the problem.

    • For instance, this programmatic escaping turns verbatim { &quot;foo&quot;: &quot;3\&quot; of snow&quot; } into verbatim { \&quot;foo\&quot;: \&quot;3\\\&quot; of snow\&quot; }, which PowerShell then encloses in &quot;...&quot; on the process command line.<sup>[1]</sup>

Some APIs accept a simplified - but not standards-compliant - form of JSON, in which property names may be unquoted, and string values may be enclosed in &#39;...&#39; rather than &quot;...&quot;

Given that your web service seemingly accepts this simplified format, using it would indeed avoid the &quot;-related bug (assuming that there aren't any property values with embedded &quot;).

While ConvertFrom-Json can read this simplified format too (in both PowerShell editions), ConvertTo-Json cannot create it (in neither edition).

Below is a custom implementation that creates this format, ConvertTo-SimplifiedJson; assuming it is already defined, the following:

&#39;{&quot;ISOYear&quot;:[{&quot;ISOYear&quot;:2023}],&quot;ISOWeek&quot;:[{&quot;ISOWeek&quot;:23}],&quot;SkillCategory&quot;:&quot;Warehouse&quot;,&quot;Building&quot;:&quot;ABC&quot;}&#39; |
  ConvertFrom-Json |
  ConvertTo-SimplifiedJson

outputs:

{ISOYear:[{ISOYear:2023}],ISOWeek:[{ISOWeek:23}],SkillCategory:&#39;Warehouse&#39;,Building:&#39;ABC&#39;}

ConvertTo-SimplifiedJson source code:
# Creates a simplified JSON representation for the given object (graph)
# using unquoted property names, where possible, and single-quoting for string values.
# Note: 
#  * The resulting respresentation is compressed (no whitespace for readability).
#  * Property values of types that cannot be represented in JSON are represented
#    as strings:
#    * [datetime] and [datetimeoffset] instances are stringified as ISO 8601 timestamps,
#       using .ToString(&#39;o&#39;)
#    * All other such types are represented by their .ToString() values.
function ConvertTo-SimplifiedJson {
  param([Parameter(ValueFromPipeline)] [object] $InputObject)
  begin {
    function test-ListLike { 
      param($o) 
      $o -is [System.Collections.IEnumerable] -and $o -isnot [string] -and $o -isnot [System.Collections.IDictionary] 
    }
  }
  process {
    $properties = 
      if ($InputObject -is [System.Collections.IDictionary]) {
        $InputObject.GetEnumerator()
      } else {
        $InputObject.psobject.Properties
      }
    if (test-ListLike $InputObject) {
      # array as single input object -&gt; recurse
      &#39;[&#39; + $(foreach ($o in $InputObject) { ConvertTo-SimplifiedJson $o }) + &#39;]&#39;
    }
    elseif ($InputObject.GetType() -in [datetime], [datetimeoffset]) {
      &quot;&#39;{0}&#39;&quot; -f $InputObject.ToString(&#39;o&#39;) # Use an ISO 8601 time string
    }
    elseif ($InputObject -is [bool]) {
      &quot;$($InputObject)&quot;.ToLower() # JSON requires case-exact &#39;true&#39; or &#39;false&#39;
    }
    elseif ($InputObject.GetType() -in [byte], [sbyte], [int16], [uint16], [int32], [uint32], [int64], [uint64], [bigint], [decimal], [float], [double] ) {
      # A number type.
      [string] $InputObject # Use culture-invariant stringification, to be embedded unquoted in the JSON.
    }
    elseif ($InputObject.GetType() -in [char], [string] -or $properties.Count -eq 0) {
      # A char., a string, or an unsupported property-less object.
      &quot;&#39;{0}&#39;&quot; -f ($InputObject -replace &quot;&#39;&quot;, &quot;\&#39;&quot;)
    }
    else { 
      # A (nested) object that doesn&#39;t map onto a JSON primitive.
      # Recursively process its properties.
      $sep = &#39;{&#39;
      -join $(
        foreach ($p in $properties) {
          $name = if ($p.Name -notmatch &#39;\W&#39;) { $p.Name } else { &quot;&#39;{0}&#39;&quot; -f ($p.Name -replace &quot;&#39;&quot;, &quot;\&#39;&quot;) }
          $value = ConvertTo-SimplifiedJson $p.Value
          &#39;{0}{1}:{2}&#39; -f $sep, $name, $value
          $sep = &#39;,&#39;
        }
      ) + &#39;}&#39;
    }
  }
}

<sup>[1] That is, the explicit escaping does what PowerShell should always have done itself, behind the scenes, and now does in v7.3+. Prior to v7.3, it blindly enclosed arguments containing spaces in &quot;...&quot;, without ever escaping embedded &quot;. It is this broken behavior that the explicit-escaping workaround builds on.</sup>

huangapple
  • 本文由 发表于 2023年7月28日 00:45:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76781874.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定