[英]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.