Конвертировать CSV в формате JS в CSV с разделителями, используя powershell - PullRequest
0 голосов
/ 11 марта 2020

У меня есть образец CSV-файла и попытался преобразовать в CSV-формате с разделителями с помощью PowerShell. Для временной метки она по умолчанию сохранялась в секундах, задаваясь вопросом, можно ли ее преобразовать в «чч: мм»

Не слишком уверен, с чего мне начать.

Спасибо за помощь!

sample.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"
   }
}

Ожидаемый результат с преобразованием отметки времени по умолчанию

enter image description here

Или, если возможно, можно добавить дату «2020-03-11», проанализированную из «Пуск»: «2020-03-11T00: 00: 00 + 11: 00» перед преобразованным временем, чтобы сделать DateTimestamp для каждой строки.

enter image description here

Ответы [ 2 ]

2 голосов
/ 11 марта 2020

Ваш входной файл является 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.

Также обратите внимание, как метки времени в JSON анализируются с помощью ConvertFrom-Json зависит от версии PowerShell (версия):

  • Windows PowerShell анализирует их как строки , поэтому достаточно разбить строка на T и взять то, что стоит перед ней.

  • PowerShell [Core] анализирует их как [datetime] экземпляров, выраженных в local время, поэтому они гарантированно приведут к тому же календарному дню, только если местный часовой пояс совпадает с часовым поясом, подразумеваемым смещением UT C в значениях JSON (+11:00).


Дополнительное чтение: соображения производительности :

Обратите внимание на использование вложенных циклов foreach по сравнению с использованием ForEach-Object командлет в конвейере для лучшей производительности Анс. См. этот ответ для справочной информации.

С небольшими входными файлами, которые могут не иметь значения, однако, и RoadRunner's полезная хеш-таблица альтернатива , который использует вложенный конвейер, вполне может быть достаточно быстрым на практике - и его тоже можно использовать для использования foreach loop ( update : теперь это происходит во второй команде) ,

Разбор JSON в хеш-таблицы ([hashtable], он же System.Collections.Hashtable) через
-AsHashtable:

  • имеет преимущество в том, что требуется меньше памяти (внутреннее хранилище экземпляров [pscustomobject], которые ConvertFrom-Json выводит по умолчанию, несколько неэффективно).

  • имеет потенциальный недостаток несохранения порядка ввода свойств, учитывая, что [hashtable] записи изначально неупорядочены; в данном случае, однако, это не проблема, поскольку создаются разные выходные объекты с фиксированным порядком свойств.

2 голосов
/ 11 марта 2020

Предполагая, что ваши данные на самом деле JSON файл, а не файл CSV, вы можете попробовать подход ниже. Если в основном преобразовать файл JSON в System.Collections.Hashtable объект с ConvertFrom-Json с помощью переключателя -AsHashTable. Вы можете прочитать, как перебирать свойства хеш-таблицы из Цикл по ха sh или использовать массив в PowerShell .

Затем вы можете получить System.Management.Automation.PSCustomObject строки и канал к Export-Csv, который создает файл CSV. Кроме того, вы можете получить временной интервал, используя [System.Timespan]::FromSeconds(), который преобразует общее количество секунд в объект типа System.Timespan и формата hh:mm с System.TimeSpan.ToString(). Для получения дополнительной информации о форматировании временного интервала вы можете взглянуть на Преобразование секунд в формат чч: мм: сс, fff в PowerShell .

В качестве дополнительного этапа очистки я также пошел и удалил " котировки также 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 '"')

sample.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 '"')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...