【问题标题】:Why doesn't this string serialize to a simple JSON string in PowerShell为什么此字符串在 PowerShell 中不序列化为简单的 JSON 字符串
【发布时间】:2021-05-31 03:58:19
【问题描述】:

见下文,$a$s 都是包含文本 "String" 的字符串,但每个使用 ConvertTo-JSON 的序列化方式不同。

为什么$s | ConvertToJson 不生成"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.ps1parameterValue,使用 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"

【问题讨论】:

    标签: json powershell


    【解决方案1】:

    PowerShell 的ETS (Extended Type System) 允许您使用附加 属性(只能由PowerShell 代码直接访问)来装饰任何对象。

    如果您使用 [string] 实例执行此操作(无论您自己执行还是其他命令为您执行[1]),这些附加属性将在使用 @ 序列化对象时出现987654326@:

    # 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
    

    以上产量:

    hi
    ---
    {
      "value": "hi",
      "foo": "bar"
    }
    

    This GitHub issue 讨论了这种令人惊讶的行为。

    解决方法

    # .psobject.BaseObject returns the underlying, undecorated object.
    PS> $decoratedString.psobject.BaseObject | ConvertTo-Json
    hi
    

    [1] 正如js2010 指出的那样,数据检索PowerShell 提供程序cmdlet - Get-ChildItemGet-ItemGet-Content、... - 都在其输出的对象中添加固定数量的NoteProperty成员,即PSPathPSParentPathPSChildNamePSDrivePSProvider

    因此,如果您序列化使用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",
    ...
    

    请注意,在字符串的情况下,这些额外的属性在构造 new 字符串时会丢失,无论是通过字符串连接还是通过应用字符串运算符(例如 @) 987654341@:

    # 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
    

    还请注意,这种每个输出对象的装饰会增加相当多的内存和性能开销;对于Get-ContentGitHub issue #7537 建议提供选择退出

    【讨论】:

    • 请注意,使用 '>' '>>' 或 'out-file' 在 PS 5 中默认将文件的编码转换为 unicode,甚至可以混合编码。
    • 我实际上发现自己使用 ConvertTo-JSON 进行调试,以查看“真正”的变量是什么,而不是终端呈现的变量 - 鉴于 VSCode 的监视调试器本身有问题并且不会显示很多可能变量。有更好的方法来检查变量吗?
    • @Marc:您可以使用Select-Object *,它使用输入对象的所有属性的静态NoteProperty副本创建输入对象的浅[pscustomobject]克隆,所有这些都基于通常的规则呈现(= 5 = 列表) - 但请注意,在字符串和原始类型的情况下,您不会看到输入对象的内在价值。在 VSCode 中,您可以将 .psobject.properties 附加到 Watch 窗口中的变量以有效地获取相同的信息,但请注意表示将是一长行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-14
    • 2012-10-21
    • 1970-01-01
    • 2012-08-15
    相关资源
    最近更新 更多