【问题标题】:Powershell ConvertTo-Json missing square brackets and adding new charactersPowershell ConvertTo-Json 缺少方括号并添加新字符
【发布时间】:2019-09-22 16:21:43
【问题描述】:

在贡献者@mklement0 和@Theo 的帮助下,我可以通过这个问题Powershell ConvertTo-Json problem with double-quotation marks 中解释的解决方案获得良好的结果。

但由于 JSON 格式的要求,我还有两个问题:

  1. mac 和 ip 地址的值应在方括号 ([ ]) 之间表示,每个值应在 (" ") 之间表示,例如:
"mac_address":  ["00:10:XX:10:00:0X", "X0:X0:11:X0:00:0X", "X0:11:X0:11:XX:11"]
  1. file.txt中,我还会有以下key-value信息:
product_executable : "C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE

我在上面提到的问题中使用了当前代码,我在开头得到了双反斜杠和一个新的:

"product_executable":  "\"C:\\Program Files (x86)\\McAfee\\VirusScan Enterprise\\SHSTAT.EXE\" /!REMEDIATE"

我已经尝试处理哈希表输出 ($oht) 并尝试修改添加和删除字符。

我的 file.txt 包含以下结构化信息(开头也有两个空行):



adapter_name          : empty1
route_age             : 10
route_nexthop         : 172.0.0.1
route_protocol        : NETMGMT1
speed                 : null 
mac_address           : 11:10:XX:10:00:0X, X1:X0:11:X0:00:0X, X1:11:X0:11:XX:11
product_executable    : "C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE

adapter_name          : empty2
route_age             : 100
route_nexthop         : 172.0.0.2
route_protocol        : NETMGMT2
speed                 : null 
mac_address           : 22:10:XX:10:00:0X, X2:X0:11:X0:00:0X, X2:11:X0:11:XX:11
product_executable    : "C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE

adapter_name          : empty3
route_age             : 1000
route_nexthop         : 172.0.0.3
route_protocol        : NETMGMT3
speed                 : null 
mac_address           : 33:10:XX:10:00:0X, X3:X0:11:X0:00:0X, X3:11:X0:11:XX:11
product_executable    : "C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE

当前代码(见提到的问题)是:

# Read the input file as a whole (-Raw) and split it into blocks (paragraphs)
(Get-Content -Raw C:\scripts\file.txt) -split '\r?\n\r?\n' -ne '' |
  ForEach-Object { # Process each block
    # Initialize an ordered hashtable for the key-values pairs in this block.
    $oht = [ordered] @{}
    # Loop over the block's lines.
    foreach ($line in $_ -split '\r?\n' -ne '') {
      # Split the line into key and value...
      $key, $val = $line -split ':', 2
      # ... and add them to the hashtable.
      $oht[$key.Trim()] = $val.Trim()
    }
    $oht # Output the hashtable.
  } | ConvertTo-Json

实际结果是:

    [
      {
        "adapter_name"         : "empty1",
        "route_age"            : 10,
        "route_nexthop"        : "172.0.0.1",
        "route_protocol"       : "NETMGMT1",
        "speed"                : null,
        "mac_address"          : "11:10:XX:10:00:0X, X1:X0:11:X0:00:0X, X1:11:X0:11:XX:11",
        "product_executable"   : "\"C:\\Program Files (x86)\\McAfee\\VirusScan Enterprise\\SHSTAT.EXE" /!REMEDIATE"
      },
      {
        "adapter_name"         : "empty2",
        "route_age"            : 100,
        "route_nexthop"        : "172.0.0.2",
        "route_protocol"       : "NETMGMT2",
        "speed"                : null,
        "mac_address"          : "22:10:XX:10:00:0X, X2:X0:11:X0:00:0X, X2:11:X0:11:XX:11",
        "product_executable"   : "\"C:\\Program Files (x86)\\McAfee\\VirusScan Enterprise\\SHSTAT.EXE" /!REMEDIATE"
      },
      {
        "adapter_name"         : "empty3",
        "route_age"            : 1000,
        "route_nexthop"        : "172.0.0.3",
        "route_protocol"       : "NETMGMT3",
        "speed"                : null,
        "mac_address"          : "33:10:XX:10:00:0X, X3:X0:11:X0:00:0X, X3:11:X0:11:XX:11",
        "product_executable"   : "\"C:\\Program Files (x86)\\McAfee\\VirusScan Enterprise\\SHSTAT.EXE" /!REMEDIATE"
      }
    ]

而预期的结果是:

    [
      {
        "adapter_name"         : "empty1",
        "route_age"            : 10,
        "route_nexthop"        : "172.0.0.1",
        "route_protocol"       : "NETMGMT1",
        "speed"                : null,
        "mac_address"          :  ["11:10:XX:10:00:0X", "X1:X0:11:X0:00:0X", "X1:11:X0:11:XX:11"],
        "product_executable"   : ""C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE"
      },
      {
        "adapter_name"         : "empty2",
        "route_age"            : 100,
        "route_nexthop"        : "172.0.0.2",
        "route_protocol"       : "NETMGMT2",
        "speed"                : null,
        "mac_address"          :  ["22:10:XX:10:00:0X", "X2:X0:11:X0:00:0X", "X2:11:X0:11:XX:11"],
        "product_executable"   : ""C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE"
      },
      {
        "adapter_name"         : "empty3",
        "route_age"            : 1000,
        "route_nexthop"        : "172.0.0.3",
        "route_protocol"       : "NETMGMT3",
        "speed"                : null,
        "mac_address"          :  ["33:10:XX:10:00:0X", "X3:X0:11:X0:00:0X", "X3:11:X0:11:XX:11"],
        "product_executable"   : ""C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE"
      }
    ]

非常感谢您的建议。

【问题讨论】:

    标签: powershell


    【解决方案1】:

    JSON 中的字符串可以采用转义序列。指定转义序列的字符是反斜杠\

    转义序列可用于以下方面:

    • 插入不可打印或空白字符(如 TAB 或换行符或null
    • 在字符串中插入双引号"(因为双引号既是字符串的开头又是字符串的结尾,你必须有办法说“我希望这个引号成为字符串的part ,而不是终止它)。
    • 插入文字反斜杠 \(因为 backlsash 是转义序列的开头,您需要一种方式说“我希望这个反斜杠成为字符串的 part,而不是开始转义序列)

    因此,在您的示例中,您看到的是:

    "\"C:\\Program Files (x86)\\McAfee\\VirusScan Enterprise\\SHSTAT.EXE\" /!REMEDIATE"

    1. 在开头,您有一个双引号 " 来启动 JSON 字符串。
    2. 然后在您拥有\" 之后立即显示“此字符串中的第一个字符是实际的"
    3. 在本身由反斜杠 \ 分隔的路径中,您需要它是双倍的,以便可以将其解释为单个反斜杠,而不是尝试将其解释为转义序列 \P rogram Files \McAfee\VirusScan
    4. SHSTAT.EXE 的末尾,您会看到下一个\",它包含在结束您引用的可执行字符串的文字引号中。

    长话短说,一切都按预期进行。当您反序列化 JSON 时,一切都会以应有的方式出现!

    想看看吗?

    $myString = @'
    "C:\Program Files (x86)\McAfee\VirusScan Enterprise\SHSTAT.EXE" /!REMEDIATE
    '@
    
    Write-Host $myString
    
    $myJsonString = $myString | ConvertTo-Json
    
    Write-Host $myJsonString
    
    $undoJson = $myJsonString | ConvertFrom-Json
    
    Write-Host $undoJson
    

    【讨论】:

    • 嗨@briantist!非常感谢您的及时支持和解释!详细介绍这个话题对我来说非常有趣。抱歉,由于行程很长,之前无法回答。我正在准备powershell获取的信息,目的是加载到Web应用程序中。因此,关于 JSON 格式的所有内容对我来说都很重要。
    【解决方案2】:

    briantist's helpful answer 表明 product_executable 属性值 正确地进行了 JSON 编码。

    至于希望将mac_address 转换为字符串的数组:所需要的只是通过分隔符字符串 将值拆分为数组:

    也就是说,而不是:

    $oht[$key.Trim()] = $val.Trim()
    

    使用

    $oht[$key.Trim()] = $($val.Trim() -split ', ')
    

    如果元素之间的空白数量可能是可变的,请使用
    -split ',\s*'

    $(...) 确保如果 -split 操作仅返回 1 个元素 - 即,如果输入不包含 - 输入字符串按原样返回,而不是作为单元素数组返回。

    以上假设:

    • 所有包含 的属性值都应该被解析为数组
    • 如果mac_address 恰好包含一个条目,则应将其解析为标量。

    以下变体仅将数组解析应用于属性mac_address,并且总是将其值解析为数组(您可以再次将-split 操作与$(...) 括起来以更改它):

    $oht[$key.Trim()] = 
      if ($key.Trim() -eq 'mac_address') { $val.Trim() -split ', ' } else { $val.Trim() }
    

    【讨论】:

    • 再次感谢您的及时答复!抱歉,由于行程很长,我之前无法回答。我刚刚测试了它,代码非常有效,如果其他键值对有多个地址。由于其他不同的原因,经过一些测试,我还必须通过添加 ConvertTo-Json | 来指定 json 文件的编码类型。 Out-File -Encoding utf8 C:\scripts\file.json,因为只有ConvertTo-Jsonfile.json utf16定义的格式。只是提一下,如果有人对我意识到的编码类型有其他要求。
    • @JuanDiego:很高兴听到这个消息。字符编码仅在您将输出发送到 file 时才起作用,不幸的是,Windows PowerShell 在这方面非常不一致(PowerShell Core 中已修复的问题, 现在始终默认为无 BOM 的 UTF-8) - 请参阅 this answer
    【解决方案3】:

    您需要将 mac 地址转换为数组。

    # Read the input file as a whole (-Raw) and split it into blocks (paragraphs)
    (Get-Content -Raw C:\scripts\file.txt) -split '\r?\n\r?\n' -ne '' |
      ForEach-Object { # Process each block
        # Initialize an ordered hashtable for the key-values pairs in this block.
        $oht = [ordered] @{}
        # Loop over the block's lines.
        foreach ($line in $_ -split '\r?\n' -ne '') {
          # Split the line into key and value...
          $key, $val = $line -split ':', 2
          # ... and add them to the hashtable.
          if ($key -like "*mac_address*"){
            $val = @($val.Replace(' ','').split(','))
          }
          $oht[$key.Trim()] = $val.Trim()
        }
        $oht # Output the hashtable.
      } | ConvertTo-Json
    

    【讨论】:

    • 嗨@Mike Frank!感谢您的及时答复!抱歉,由于行程很长,我之前无法回答。您的代码也得到了预期的结果。也许如果有更多带有 ip 或 mac 地址的键值对,那么代码可能会更长一点,以便事先知道该键是否具有 ip 或 mac 地址值。但是如果我用 _address 命名所有包含地址的键,我将能够要求所有键-addresses,例如:if ($key -like "*_address*")*
    猜你喜欢
    • 2018-02-12
    • 2015-05-25
    • 1970-01-01
    • 2021-10-17
    • 2021-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多