PowerShell 7.2:ConvertFrom-Json-Date处理

m4pnthwp  于 2022-11-10  发布在  Shell
关注(0)|答案(4)|浏览(165)

在Powershell7.2中,JSON反序列化为对象的方式似乎有了变化-->而不是字符串,现在是DateTime。但我希望具有“旧”行为,即将其作为字符串而不是DateTime处理。
在Powershell7.2中使用ConvertFrom-Json时,如何将所有日期反序列化为字符串而不是日期时间?

编辑:

$val = '{ "date":"2022-09-30T07:04:23.571+00:00" }' | ConvertFrom-Json
$val.date.GetType().FullName
qvtsj1bj

qvtsj1bj1#

这实际上是一个已知问题,请参阅:#13598 Add a -DateKind parameter to ConvertFrom-Json to control how System.DateTime / System.DateTimeOffset values are constructed。然而,我认为这个问题没有简单的解决方案。你可以做的一件事就是调用(Windows)PowerShell。它目前也不是直接向前的,因此我创建了一个小 Package 来在PowerShell会话之间发送和接收复杂对象(另请参阅我的#18460 Invoke-PowerShell目的):

function Invoke-PowerShell ($Command) {
    $SerializeOutput = @"
         `$Output = $Command
         [System.Management.Automation.PSSerializer]::Serialize(`$Output)
"@
    $Bytes = [System.Text.Encoding]::Unicode.GetBytes($SerializeOutput)
    $EncodedCommand = [Convert]::ToBase64String($Bytes)
    $PSSerial = PowerShell -EncodedCommand $EncodedCommand
    [System.Management.Automation.PSSerializer]::Deserialize($PSSerial)
}

用途:

Invoke-PowerShell { '{ "date":"2022-09-30T07:04:23.571+00:00" }' | ConvertFrom-Json }

date
----
2022-09-30T07:04:23.571+00:00

更新

正如**mklement0**所评论的那样,我显然将答案复杂化了。
通过Powershell.exe调用是一种实用的解决办法(尽管速度很慢且仅适用于Windows),但请注意,您不需要帮助器函数:如果您从PowerShell向Powershell.exe(或pwsh.exe)传递脚本块,基于Based64 CLIXML的序列化将在后台自动发生:出于这个原因,尝试powershell.exe -noprofile { $args | ConvertFrom-Json } -args '{ "date":"2022-09-30T07:04:23.571+00:00" }',我认为不需要Invoke-PowerShell cmdlet。

$Json = '{ "date":"2022-09-30T07:04:23.571+00:00" }'
powershell.exe -noprofile { $args | ConvertFrom-Json } -args $Json

date
----
2022-09-30T07:04:23.571+00:00
des4xlb0

des4xlb02#

  • iRon's helpful answer**通过Windows PowerShellCLI powershell.exe提供了一个实用的解决方案,基于ConvertFrom-Json没有自动将类似ISO 8601的时间戳字符串转换为[datetime]示例。
  • 希望他链接到的GitHub问题中的建议#13598将在未来实施,从而将解决方案简化为:

# NOT YET IMPLEMENTED as of PowerShell 7.2.x

 '{ "date":"2022-09-30T07:04:23.571+00:00" }' |
   ConvertFrom-Json -DateTimeKind None
  • 然而,powershell.exe解决方法有两个缺点:(A)它很慢(必须在子进程中启动单独的PowerShell示例),以及(B)它仅在Windows上运行。下面的解决方案是您自己避免这些问题的方法的概括。

以下是your own in-process approach推广

  • 它在每个匹配时间戳模式的字符串的开头注入NUL字符("0"`)-假设输入本身从不包含这样的字符,这是公平的假设。
  • 与您的方法一样,这会阻止ConvertFrom-Json识别时间戳字符串,并使其保持不变。
  • 然后必须对ConvertFrom-Json输出的[pscustomobject]图形进行后处理,以便再次删除注入的NUL字符。
  • 这是通过ForEach-Object调用实现的,该调用包含一个帮助脚本块,该块递归地遍历对象图,其优点是**与JSON输入一起工作,其时间戳字符串可以是在层次结构的任何级别(即,它们也可以在嵌套对象的属性中)。
  • 注意**:假设时间戳字符串只作为属性值包含在输入中;如果您也想处理输入JSON,如'[ "2022-09-30T07:04:23.571+00:00" ]',则需要做更多的工作,其中字符串本身就是输入对象。

# Sample JSON.

$val = '{ "date":"2022-09-30T07:04:23.571+00:00" }'

$val -replace '"(?=\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}")', "`"`0" |          #"
  ConvertFrom-Json |
  ForEach-Object {
    # Helper script block that walks the object graph
    $sb = {
      foreach ($o in $args[0]) { 
        if ($o -is [Array]) { # nested array -> recurse
          foreach ($el in $o) { & $sb $el } # recurse
        }
        elseif ($o -is [System.Management.Automation.PSCustomObject]) {
          foreach ($prop in $o.psobject.Properties) { 
            if ($prop.Value -is [Array]) {
              foreach ($o in $prop.Value) { & $sb $o } # nested array -> recurse
            }
            elseif ($prop.Value -is [System.Management.Automation.PSCustomObject]) { 
              & $sb $prop.Value # nested custom object -> recurse
            }
            elseif ($prop.Value -is [string] -and $prop.Value -match '^\0') { 
              $prop.Value = $prop.Value.Substring(1) # Remove the NUL again.
            }
          } 
        }
      }
    }
    # Call the helper script block with the input object.
    & $sb $_
    # Output the modified object.
    if ($_ -is [array]) {
      # Input object was array as a whole (implies use of -NoEnumerate), output as such.
      , $_ 
    } else {
      $_
    }
  }
nhaq1z21

nhaq1z213#

根据@zett42的输入,我的解决方案如下:
假设我们知道JSON中使用的日期的正则表达式模式,我将JSON作为字符串,添加一个前缀,以便ConvertFrom-Json不会将日期转换为日期时间,而是将其保留为字符串,使用ConvertFrom-Json将其转换为PSCustomObject,对对象执行我需要做的任何操作,将其序列化为带有ConvertTo-Json的JSON字符串,然后再次删除前缀。

[string]$json = '{ "date":"2022-09-30T07:04:23.571+00:00", "key1": "value1" }'

[string]$jsonWithDatePrefix = $json -replace '"(\d+-\d+.\d+T\d+:\d+:\d+\.\d+\+\d+:\d+)"', '"#$1"'

[pscustomobject]$jsonWithDatePrefixAsObject = $jsonWithDatePrefix | ConvertFrom-Json

$jsonWithDatePrefixAsObject.key1 = "value2"

[string]$updatedJsonString = $jsonWithDatePrefixAsObject | ConvertTo-Json

[string]$updatedJsonStringWithoutPrefix = $updatedJsonString -replace '"(#)(\d+-\d+.\d+T\d+:\d+:\d+\.\d+\+\d+:\d+)"', '"$2"'

Write-Host $updatedJsonStringWithoutPrefix
u91tlkcl

u91tlkcl4#

更改日期格式的另外两种方法:

Get-Node

使用类似于**mklement0**递归函数的Get-Node

$Data = ConvertFrom-Json $Json
$Data |Get-Node -Where { $_.Value -is [DateTime] } | ForEach-Object {
    $_.Value  = GetDate($_.Value) -Format 'yyyy-MM-ddTHH\:mm\:ss.fffzzz' -AsUTC
}
$Data

DIY

或者自己动手构建自己的Json反序列化程序:

function ConvertFrom-Json {
    [CmdletBinding()][OutputType([Object[]])] param(
        [Parameter(ValueFromPipeLine = $True, Mandatory = $True)][String]$InputObject,
        [String]$DateFormat = 'yyyy-MM-ddTHH\:mm\:ss.fffffffzzz', # Default: ISO 8601, https://www.newtonsoft.com/json/help/html/datesinjson.htm
        [Switch]$AsLocalTime,
        [Switch]$AsOrdered
    )
    function GetObject($JObject) {
        switch ($JObject.GetType().Name) {
            'JValue' {
                switch ($JObject.Type) {
                    'Boolean'  { $JObject.Value }
                    'Integer'  { 0 + $JObject.Value }                                                 # https://github.com/PowerShell/PowerShell/issues/14264
                    'Date'     { Get-Date $JObject.Value -Format $DateFormat -AsUTC:(!$AsLocalTime) } # https://github.com/PowerShell/PowerShell/issues/13598
                    Default    { "$($JObject.Value)" }
                }
            }
            'JArray' {
                ,@( $JObject.ForEach{ GetObject $_ } )
            }
            'JObject' {
                $Properties = [Ordered]@{}
                $JObject.ForEach{ $Properties[$_.Name] = GetObject $_.Value }
                if ($AsOrdered) { $Properties } else { [PSCustomObject]$Properties }                  # https://github.com/PowerShell/PowerShell/pull/17405
            }
        }
    }
    GetObject ([Newtonsoft.Json.Linq.JObject]::Parse($InputObject))
}

用法:

ConvertFrom-Json $Json -DateFormat 'yyyy-MM-ddTHH\:mm\:ss.fffzzz' |ConvertTo-Json -Depth 9

相关问题