[英]Convert JS format csv to a delimited CSV using powershell
我有一个示例 csv 文件,并尝试使用 powershell 转换为分隔格式的 csv。 对于时间戳部分,默认存储为秒,想知道能不能转成"hh:mm"
不太确定我应该从哪里开始。
感谢帮助!
样本.csv
{
"Body" : {
"inverter/1" : {
"Data" : {
"Current_DC_String_1" : {
"Unit" : "A",
"Values" : {
"0" : 0,
"300" : 0,
"600" : 0,
"900" : 0
},
"_comment" : "channelId=66050"
},
"Current_DC_String_2" : {
"Unit" : "A",
"Values" : {
"0" : 0,
"300" : 0,
"600" : 0,
"900" : 0
},
"_comment" : "channelId=131586"
},
"EnergyReal_WAC_Sum_Produced" : {
"Unit" : "Wh",
"Values" : {
"0" : 0,
"300" : 0,
"600" : 0,
"900" : 0
},
"_comment" : "channelId=67830024"
},
"Voltage_DC_String_1" : {
"Unit" : "V",
"Values" : {
"0" : 7.3000000000000007,
"300" : 7.3000000000000007,
"600" : 7.9000000000000004,
"900" : 7.7000000000000002
},
"_comment" : "channelId=66049"
},
"Voltage_DC_String_2" : {
"Unit" : "V",
"Values" : {
"0" : 4.2000000000000002,
"300" : 4.2000000000000002,
"600" : 4.5,
"900" : 4.4000000000000004
},
"_comment" : "channelId=131585"
}
},
"DeviceType" : 233,
"End" : "2020-03-11T23:59:59+11:00",
"NodeType" : 97,
"Start" : "2020-03-11T00:00:00+11:00"
},
"inverter/2" : {
"Data" : {
"Current_DC_String_1" : {
"Unit" : "A",
"Values" : {
"0" : 0,
"300" : 0,
"600" : 0,
"900" : 0
},
"_comment" : "channelId=66050"
},
"Current_DC_String_2" : {
"Unit" : "A",
"Values" : {
"0" : 0,
"300" : 0,
"600" : 0,
"900" : 0
},
"_comment" : "channelId=131586"
},
"EnergyReal_WAC_Sum_Produced" : {
"Unit" : "Wh",
"Values" : {
"0" : 0,
"300" : 0,
"600" : 0,
"900" : 0
},
"_comment" : "channelId=67830024"
},
"Voltage_DC_String_1" : {
"Unit" : "V",
"Values" : {
"0" : 6.7000000000000002,
"300" : 7,
"600" : 6.8000000000000007,
"900" : 7.2000000000000002
},
"_comment" : "channelId=66049"
},
"Voltage_DC_String_2" : {
"Unit" : "V",
"Values" : {
"0" : 2.2000000000000002,
"300" : 2.3000000000000003,
"600" : 2.2000000000000002,
"900" : 2.2000000000000002
},
"_comment" : "channelId=131585"
}
},
"DeviceType" : 233,
"End" : "2020-03-11T23:59:59+11:00",
"NodeType" : 98,
"Start" : "2020-03-11T00:00:00+11:00"
}
},
"Head" : {
"RequestArguments" : {
"Query" : "Inverter+SensorCard+Meter",
"Scope" : "System"
},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2020-03-11T01:00:03+11:00"
}
}
转换默认时间戳的预期结果
或者,如果可能,可以在转换时间前添加从“开始”解析的日期“2020-03-11”:“2020-03-11T00:00:00+11:00”,为每一行制作 DateTimestamp。
假设您的数据实际上是一个 JSON 文件,而不是一个 CSV 文件,您可以尝试以下方法。 如果基本上使用-AsHashTable
开关使用ConvertFrom-Json
将 JSON 文件转换为System.Collections.Hashtable
对象。 您可以从循环遍历哈希或在 PowerShell 中使用数组阅读如何遍历哈希表属性。
然后,您可以获取System.Management.Automation.PSCustomObject
行和管道到Export-Csv
,这将创建 CSV 文件。 此外,您可以使用[System.Timespan]::FromSeconds()
获取时间跨度,它将总秒数转换为System.Timespan
类型的对象并使用System.TimeSpan.ToString()
格式为hh:mm
。 有关时间跨度格式的更多信息,您可以查看在 PowerShell 中将秒转换为 hh:mm:ss,fff 格式。
作为额外的清理步骤,我还删除了带有Set-Content
的"
引号。如果您希望"
保留在您的文件中,则没有必要这样做。
$json = Get-Content -Path .\sample.json | ConvertFrom-Json -AsHashtable
$json.Body.GetEnumerator() | ForEach-Object {
$inverter = $_.Key
$_.Value.Data.GetEnumerator() | ForEach-Object {
$value = $_.Key
$_.Value.Values.GetEnumerator() | ForEach-Object {
[PSCustomObject]@{
Inverter = "$inverter $value"
Second = [timespan]::FromSeconds($_.Key).ToString("hh\:mm")
Value = $_.Value
}
}
}
} | Export-Csv -Path .\sample.csv
# Use NoTypeINformation to remove #TYPE from headers in < Powershell 6
Set-Content -Path .\sample.csv -Value ((Get-Content -Path .\sample.csv) -replace '"')
样本.csv
Inverter,Second,Value
inverter/1 Current_DC_String_2,00:05,0
inverter/1 Current_DC_String_2,00:10,0
inverter/1 Current_DC_String_2,00:15,0
inverter/1 Current_DC_String_2,00:00,0
inverter/1 Current_DC_String_1,00:05,0
inverter/1 Current_DC_String_1,00:10,0
...
性能改进
正如mklement0解释的那样,当使用 .NET 类型或[PSCustomObject]
,成员枚举比使用管道快得多。 您可以从这个有用的答案中找到更多信息。
下面是可以使用foreach
枚举而不是Foreach-Object
进行的改进的简单用法。
$json = Get-Content -Path .\sample.json | ConvertFrom-Json -AsHashtable
$csvRows = @()
foreach ($inverter in $json.Body.GetEnumerator()) {
foreach ($outerValue in $inverter.Value.Data.GetEnumerator()) {
foreach ($innerValue in $outerValue.Value.Values.GetEnumerator()){
$csvRowData = [PSCustomObject]@{
Inverter = "$($inverter.Key) $($outerValue.Key)"
Second = [timespan]::FromSeconds($innerValue.Key).ToString("hh\:mm")
Value = $innerValue.Value
}
$csvRows += $csvRowData;
}
}
}
$csvRows | Export-Csv -Path .\sample.csv
Set-Content -Path .\sample.csv -Value ((Get-Content -Path .\sample.csv) -replace '"')
您的输入文件是 JSON 文件,而不是CSV文件。
为了将其对象图展平为您需要的 CSV 行列结构,需要嵌套循环:
# Parse the JSON file into custom objects.
$fromJson = Get-Content -Raw file.json | ConvertFrom-Json
& {
foreach ($inverter in $fromJson.Body.psobject.Properties.Name) {
$date = $fromJson.Body.$inverter.Start
if ($date -is [datetime]) { $date = $date.ToString('yyyy-MM-dd') }
else { $date = ($date -csplit 'T')[0] }
foreach ($measurement in $fromJson.Body.$inverter.Data.psobject.Properties.Name) {
foreach ($valueProp in $fromJson.Body.$inverter.Data.$measurement.Values.psobject.Properties) {
[pscustomobject] @{
Inverter = "$inverter $measurement"
TimeStamp = $date + ' ' +
[timespan]::FromSeconds([int] $valueProp.Name).ToString('hh\:mm')
Value = $valueProp.Value
}
}
}
}
} | ConvertTo-Csv # output CSV data as an array of *strings*;
# to save to a *file*, use something like:
# Export-Csv -NoTypeInformation out.csv
请注意.psobject.Properties
如何用于反映给定对象的属性; .psobject
是任何对象上可用的通常隐藏的属性,它比Get-Member
cmdlet 更方便、更快速地提供反射信息。
另请注意ConvertFrom-Json
如何解析 JSON 中的时间戳取决于 PowerShell 版本(版本):
Windows PowerShell将它们解析为strings ,因此将字符串按T
拆分并采用它之前的内容就足够了。
PowerShell [Core]将它们解析为[datetime]
实例,以本地时间表示,因此只有当本地时区与 JSON 值中的 UTC 偏移量所暗示的时区相同时,它们才能保证产生相同的日历日( +11:00
)。
可选阅读:性能考虑:
请注意在管道中使用嵌套的foreach
循环而不是使用ForEach-Object
cmdlet以获得更好的性能。 有关背景信息,请参阅此答案。
然而,对于可能无关紧要的小输入文件,以及RoadRunner 有用的基于哈希表的替代方案,它使用嵌套管道,在实践中可能足够快——而且它也可以改为使用foreach
循环(更新:现在确实如此,在第二个命令中)。
将 JSON 解析为哈希表( [hashtable]
,又名System.Collections.Hashtable
)通过
-AsHashtable
:
具有需要较少内存的优点( ConvertFrom-Json
默认输出的[pscustomobject]
实例的内部存储有些低效)。
具有不保留属性输入顺序的潜在缺点,因为[hashtable]
条目本质上是无序的; 然而,在手头的情况下,这不是问题,因为创建了具有固定属性顺序的不同输出对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.