【问题标题】:Powershell nested JSON to csv conversionPowershell 嵌套 JSON 到 csv 转换
【发布时间】:2022-01-15 08:43:38
【问题描述】:

我有一个相当奇特的嵌套 JSON,在某些情况下,键值对正常出现,但在其他情况下,键的类型出现在进一步的嵌套中。

{"metadata":{"systemId":"da1895","legalEntity":"A0"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G32"},"seg":{"string":"S"},"strike":{"double":4.4}}}
{"metadata":{"systemId":"45364d","legalEntity":"5G"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G81"},"seg":{"string":"S"},"strike":{"double":5.0}}}

在示例中您可以看到metadata 的字段是简单的键值对,但在recordContent 下方,我们有positionDate,这是一个简单的键值,但"account":{"string":"G32"}"strike":{"double":4.4} 不是.

我想放弃类型信息并得到如下的 CSV 结构:

systemId, legalEntity, positionDate,         account,seg,strike
da1895,   A0,          2019-04-08 00:00:00.0,G32,    S,  4.4
4536d,    5G,          2019-04-08 00:00:00.0,G81,    S,  5.0

关于如何使用 Powershell 将这种结构转换为 CSV 的任何想法?

这是我尝试过的:

$TemplateParametersFile = "c:\data\output.json"
$JsonParameters = Get-Content $TemplateParametersFile | ConvertFrom-Json

$metadatafields = $JsonParameters.metadata[0].PSObject.Properties.Name
$recordcontentfields = $JsonParameters.recordContent[0].PsObject.Properties.Name

$oData = New-Object PSObject

$metadatafields |
ForEach {
    Add-Member -InputObject $oData -NotePropertyName ($_) -NotePropertyValue $JsonParameters.metadata.($_)
}

$recordcontentfields |
ForEach {
    Add-Member -InputObject $oData -NotePropertyName ($_) -NotePropertyValue $JsonParameters.recordContent.($_)
}

这给了我:

$oData

systemId     : {da1895, 45364d}
legalEntity  : {A0, 5G}
positionDate : {2019-04-08 00:00:00.0, 2019-04-08 00:00:00.0}
account      : {@{string=G32}, @{string=G81}}
seg          : {@{string=S}, @{string=S}}
strike       : {@{double=4.4}, @{double=5.0}}

我现在有点卡住了,上面没有转换为 csv。

请注意,除了元数据和记录内容之外,我没有硬编码任何字段名,并且我希望在 JSON 结构发生变化时保持这种灵活性。

谢谢

【问题讨论】:

    标签: arrays json powershell csv


    【解决方案1】:

    我建议在有序的hashtable ([ordered] @{}) 中迭代地收集属性-名称-值对,然后可以将其转换为[pscustomobject] 以将其转换为自定义对象。

    没有属性 names 在以下解决方案中被硬编码,但 object-graph structure 假定遵循示例 JSON 中的模式,即仅限于一层嵌套 - 如果您需要处理任意嵌套的对象,this answer 可能是一个起点。

    反射(属性名称和值的发现)是通过 PowerShell 为所有对象提供的intrinsic .psobject property 执行的。

    # Parse sample JSON into an array of [pscustomobject] graphs.
    $fromJson = ConvertFrom-Json @'
    [
      {"metadata":{"systemId":"da1895","legalEntity":"A0"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G32"},"seg":{"string":"S"},"strike":{"double":4.4}}}
      ,
      {"metadata":{"systemId":"45364d","legalEntity":"5G"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G81"},"seg":{"string":"S"},"strike":{"double":5.0}}}
    ]
    '@
    
    # Initialize an aux. ordered hashtable to collect the property-name-value
    # pairs in.
    $oht = [ordered] @{}
    
    $fromJson | ForEach-Object {
        $oht.Clear()
        # Loop over top-level properties.
        foreach ($topLevelProp in $_.psobject.Properties) {
          # Loop over second-level properties.
          foreach ($prop in $topLevelProp.Value.psobject.Properties) {
            if ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
              # A nested value: Use the value of the (presumed to be one-and-only)
              #                 property of the object stored in the value.
              $oht[$prop.Name] = $prop.Value.psobject.Properties.Value
            }
            else {
              # A non-nested value: use as-is.
              $oht[$prop.Name] = $prop.Value
            }
          }
        }
        # Construct and output a [pscustomobject] from the aux. ordered hashtble.
        [pscustomobject] $oht
      } |
          ConvertTo-Csv  # Replace this with Export-Csv to export to a file.
    

    以上产出:

    "systemId","legalEntity","positionDate","account","seg","strike"
    "da1895","A0","2019-04-08 00:00:00.0","G32","S","4.4"
    "45364d","5G","2019-04-08 00:00:00.0","G81","S","5"
    

    【讨论】:

    • 很高兴听到这个消息,@user2474226。我在答案中使嵌套约束更加清晰,并且我添加了一个指向可以处理任意嵌套的答案的链接(原则上,不是同一个用例)。
    【解决方案2】:

    几年前,我为此编写了一个可重用的Flatten-Object 函数。
    唯一的区别是它将(子)属性名称与父属性名称组合在一起,因为它们可能不是唯一的:

    $JsonParameters |Flatten-Object |Format-Table
    

    metadata.systemId metadata.legalEntity recordContent.positionDate recordContent.account.string recordContent.seg.string recordContent.strike.double
    ----------------- -------------------- -------------------------- ---------------------------- ------------------------ ---------------------------
    da1895            A0                   2019-04-08 00:00:00.0      G32                          S                                                4.4
    45364d            5G                   2019-04-08 00:00:00.0      G81                          S                                                  5
    

    【讨论】:

    • 这非常简洁,虽然不完全是我正在寻找的,因为您看到类型(字符串或双精度)出现在最后的列名中。我会进一步研究你的函数来理解它。
    【解决方案3】:

    试试这个:

    $data = ConvertFrom-Json @"
    [
    {"metadata":{"systemId":"da1895","legalEntity":"A0"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G32"},"seg":{"string":"S"},"strike":{"double":4.4}}},
    {"metadata":{"systemId":"45364d","legalEntity":"5G"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G81"},"seg":{"string":"S"},"strike":{"double":5.0}}}
    ]
    "@
    
    $data | Select-Object -Property @{l="systemId"; e={$_.metadata.systemId}}, @{l="legalEntity"; e={$_.metadata.legalEntity}},
    @{l="positionDate"; e={$_.recordContent.positionDate}}, @{l="account"; e={$_.recordContent.account.string}},
    @{l="seg"; e={$_.recordContent.seg.string}}, @{l="strike"; e={$_.recordContent.strike.double}} | Export-Csv
    

    【讨论】:

    • 谢谢 - 但您正在硬编码字段名称并修复类型,这并没有真正帮助。我需要一种方法来忽略类型并能够灵活地选择任意数量的字段
    【解决方案4】:

    这应该适用于任何嵌套的 psobject。

    $json = @'
    {"metadata":{"systemId":"da1895","legalEntity":"A0"},"recordContent":{"positionDate":"2019-04-08 00:00:00.0","account":{"string":"G32"},"seg":{"string":"S"},"strike":{"double":4.4}}}
    
    '@
    $obj = ConvertFrom-Json $json
    
    
    $obj.recordContent | gm -MemberType NoteProperty | % {
        $prop = $_.name
        if ($obj.recordContent.$prop.GetType().name -eq 'pscustomobject') {
            $obj.recordContent.$prop = $obj.recordContent.$prop.psobject.Members | where membertype -eq noteproperty | select -ExpandProperty value
        }
        $obj.metadata | add-member -MemberType NoteProperty -Name $prop -Value $obj.recordContent.$prop
    
    }
    $newobj = $obj.metadata
    $newobj
    

    【讨论】:

    • 谢谢,但这并没有消除类型信息,也不清楚如何将其转换为 CSV?
    • 对我有用...你运行代码了吗?....导出到 csv....$newobj | export-csv c:\temp\mycsv
    • 是的,我运行了您的代码 - 只有 1 条 json 行 - 并导出到 csv 并按您所说的那样工作。但我从 json 文件中读取了 2 行(正如我在问题中所提到的)$obj = Get-Content 'output.json'|ConvertFrom-Json,然后在运行其余代码后,accountpositionDatesegstrike 字段显示为System.Object[]
    • 好的...还有其他解决方案已发布,祝您好运。
    猜你喜欢
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 2020-01-21
    • 1970-01-01
    • 1970-01-01
    • 2018-01-07
    • 2020-04-02
    相关资源
    最近更新 更多