【问题标题】:PowerShell Convert CSV to nested JsonPowerShell 将 CSV 转换为嵌套的 Json
【发布时间】:2021-01-08 15:30:04
【问题描述】:

我尝试创建一个脚本,将 csv 文件中给定的数据导入 API,为此,必须将 CSV 的每一行转换为 Json,然后发布到 API。

我在 csv 中的基础数据如下所示:

Users.Name,Users.Mail,Users.AdditionalInfo.String1,Users.AdditionalInfo.Int1
System.String,System.String,System.String,System.Int32
MyName,my@name.com,Hello,1
YourName,your@name.com,GoodBye,2

第一行包含 Json 信息,其中“用户”是数据写入的 API 资源。 “用户”也可以是通过 API 访问的任何其他对象,请参阅下面的评论。

第二行是标识值的类型。这是以后用的,暂时不用,从第三行开始是数据。

“。”用于指定嵌套的“级别”,所以第一个“.”将 API 对象与 json 中的第一级、第二个“.”分开。第一级从第二级。

我希望像这样返回 Json:

{
    "Name": "MyName",
    "Mail": "my@mail.com",
    "AdditionalInfo": {
        "String1": "Hello",
        "Int1": 1
    }
}

我使用以下代码:

$objects = Import-Csv "test.csv" -Encoding UTF8
$validObjects = $objects | Select-Object -Skip 1
$validObjects

ForEach ($object in $validObjects) {
    
    #$apiObject = ($object.psobject.properties.name).Split(".")[0]
    $jsonObject = @{}

    ForEach ($property in $object.psobject.properties) {
       
       switch($property.Name.Split(".").Count - 1) {
            1 {
                $jsonObject.Add($property.Name.Split(".")[1], $property.Value)        
            }
            2 {
                $tempJsonObject = @{$property.Name.Split(".")[2] =  $property.Value}            
                $jsonObject.Add($property.Name.Split(".")[1], $tempJsonObject)        
            }
        }   
    }

    $jsonObject | ConvertTo-Json
    #@($object.psobject.properties).Count
}

我现在面临的问题是,由于我有两个以“Users.AdditionalInfo”开头的列,因此会遇到错误,因为您只能将“AdditionalInfo”作为键添加一次。有没有简单的方法?

【问题讨论】:

  • 正如我在发布(现已删除)的答案中提到的,鉴于 CSV 数据可能适用于具有不同属性的计算机,无论是用户还是计算机,是否总是会有“附加信息”?
  • 为什么会被删除?
  • @DougMaurer OP 作为评论添加:'标题中的标签都不是固定的,例如,如果您要导入计算机,它们都将以“computers.”开头,以及不同的字段,例如"computers.name,computers.ip,..." ' 表示我的回答无效。
  • @Robin 是的,我注意到我在同一条船上。
  • @Robin,没有 AdditionalInfo 只是一个例子,可能还有更多,或者没有。

标签: json powershell csv nested


【解决方案1】:

以这种方式设置似乎很疯狂。当然,XML 将是满足需求的更好格式。也就是说,这就是我想出的。

设置示例文件,以便其他人可以自己尝试。

$tempfile = New-TemporaryFile

@'
Users.Name,Users.Mail,Users.AdditionalInfo.String1,Users.AdditionalInfo.Int1
System.String,System.String,System.String,System.Int32
MyName,my@name.com,Hello,1
YourName,your@name.com,GoodBye,2
'@ | Set-Content $tempfile -Encoding utf8

$csvdata = Import-Csv $tempfile | select -skip 1

现在这是我的疯狂脚本

$csvdata | foreach {
    $ht = [ordered]@{}
    ,@($_.psobject.properties) | foreach {
        $subprops,$props = $_.where({$_.name -match 'additionalinfo'},'split')
        $props | foreach {$ht.Add($_.name,$_.value)}
        ,@($subprops) | foreach {
            $ht.Add("AdditionalInfo",(@{
                ($_[0].name.split(".")[2]) = $_[0].value
                ($_[1].name.split(".")[2]) = $_[1].value
            }))
        }
    }
    $ht
} | ConvertTo-Json -OutVariable jsonresults

显示的输出加上存储在$jsonresults

[
    {
        "Users.Name":  "MyName",
        "Users.Mail":  "my@name.com",
        "AdditionalInfo":  {
                               "String1":  "Hello",
                               "Int1":  "1"
                           }
    },
    {
        "Users.Name":  "YourName",
        "Users.Mail":  "your@name.com",
        "AdditionalInfo":  {
                               "String1":  "GoodBye",
                               "Int1":  "2"
                           }
    }
]

我确信我从 mklement 或 Mathias 那里学到了一个用过两次的技巧

,@(some objects that normally get passed one by one) | foreach

当您添加逗号和数组构造时,它会将所有元素一次一对一地传递。在某些时候非常有帮助,肯定是这样的。

我想强调的另一个技巧是这一行

$subprops,$props = $_.where({$_.name -match 'additionalinfo'},'split')

where 方法有多种模式,一种是拆分模式。任何匹配的额外变量都进入第一个变量,其余的进入第二个变量。希望这可以帮助您完成项目。

编辑

由于诸如附加信息之类的项目可能会因子属性的数量而不同,因此这里有一个可以容纳的版本。

$csvdata | foreach {
    $ht = [ordered]@{}
    ,@($_.psobject.properties) | foreach {
        $subprops,$props = $_.where({($_.name.split("."))[2]},'split')
        $props | foreach {$ht.Add($_.name,$_.value)}
        $subs = $subprops | foreach {$_.name.split(".")[1]} | Select -Unique
        foreach($name in $subs)
        {
            ,@($subprops | where name -match $name) | foreach {
                $oht = [ordered]@{}
                $_ | foreach {$oht[$_.name.split(".")[2]] = $_.value}
                $ht.Add($name,$oht)
            }
        }
    }
    $ht
} | ConvertTo-Json -OutVariable jsonresults -Depth 5

第一个数据集输出

[
    {
        "Users.Name":  "MyName",
        "Users.Mail":  "my@name.com",
        "AdditionalInfo":  {
                               "String1":  "Hello",
                               "Int1":  "1"
                           }
    },
    {
        "Users.Name":  "YourName",
        "Users.Mail":  "your@name.com",
        "AdditionalInfo":  {
                               "String1":  "GoodBye",
                               "Int1":  "2"
                           }
    }
]

@'
Users.Name,Users.Mail,Users.AnotherPossibility.String1,Users.AnotherPossibility.Int1,Users.AnotherPossibility.Int2,Users.AdditionalInfo.String1,Users.AdditionalInfo.Int1
System.String,System.String,System.String,System.Int32,System.String,System.Int32
MyName,my@name.com,Hello,1,3,Bonjour,5
YourName,your@name.com,GoodBye,2,4,Adios,6
'@ | Set-Content $tempfile -Encoding utf8

第二个数据集输出

[
    {
        "Users.Name":  "MyName",
        "Users.Mail":  "my@name.com",
        "AnotherPossibility":  {
                                   "String1":  "Hello",
                                   "Int1":  "1",
                                   "Int2":  "3"
                               },
        "AdditionalInfo":  {
                               "String1":  "Bonjour",
                               "Int1":  "5"
                           }
    },
    {
        "Users.Name":  "YourName",
        "Users.Mail":  "your@name.com",
        "AnotherPossibility":  {
                                   "String1":  "GoodBye",
                                   "Int1":  "2",
                                   "Int2":  "4"
                               },
        "AdditionalInfo":  {
                               "String1":  "Adios",
                               "Int1":  "6"
                           }
    }
]

【讨论】:

  • 至于 XML 我支持你,但这是针对对此类事情知之甚少的客户。我知道第一个“技巧”第二个肯定有帮助,不知道这存在。您提供的解决方案按预期工作!这使我认识到一个人永远不够具体。 “additionalInfo”中的项目的数量也可以变化,可能有 0 到 10 以上,也可能有其他“hives”,如 AdditionalInfo,它们的名称不同,如“SurplusInfo”,它们可能同时存在或都不存在。
  • 我已更新脚本以满足您的需求。请测试并考虑将其标记为接受的答案,如果它按预期工作。
  • 它实际上看起来更干净了。
  • 我将行 $props | foreach {$ht.Add($_.name,$_.value)} 更改为 $subs = $subprops | foreach {$_.name.split(".")[1]} | Select -Unique 以摆脱“用户”。效果很好,但是如何删除“AnotherPossibility”和“AdditionalInfo”的方括号?所以它看起来像: "AnotherPossibility": { "String1": "GoodBye" "Int1": "2" "Int2": "4" }
  • "AnotherPossibility": { "String1": "GoodBye", "Int1": "2", "Int2": "4" } 缺少逗号
猜你喜欢
  • 2022-01-15
  • 2020-01-21
  • 1970-01-01
  • 2018-01-07
  • 2020-10-28
  • 2020-04-02
  • 2018-10-27
  • 2021-05-17
相关资源
最近更新 更多