PowerShell для преобразования вложенных JSON-массив на отдельные столбцы в CSV-файл
У меня есть файл JSON, который выглядит следующим образом:
{
"id": 10011,
"title": "Test procedure",
"slug": "slug",
"url": "http://test.test",
"email": "test@test.com",
"link": "http://test.er",
"subject": "testing",
"level": 1,
"disciplines": [
"discipline_a",
"discipline_b",
"discipline_c"
],
"areas": [
"area_a",
"area_b"
]
},
Я пытался использовать следующую команду, чтобы преобразовать его в CSV-файл:
(Get-Content "PATH_TOtest.json" -Raw | ConvertFrom-Json)| Convertto-CSV -NoTypeInformation | Set-Content "PATH_TOtest.csv"
Однако для дисциплин и областей я получаю систему.Объект[] в результирующем файле CSV.
Есть ли способ поместить все эти вложенные значения в виде отдельных столбцов в CSV-файл, например area_1, area_2 и т. д. И то же самое касается дисциплин.
2 ответа:
Командлеты преобразования/экспорта CSV не могут "сгладить" объект, и я могу что-то упустить, но я не знаю способа сделать это со встроенным командлетом или функцией. Если вы можете гарантировать, что
disciplines
иareas
всегда будут иметь одинаковое число элементов, вы можете упростить его, используяSelect-Object
с производными свойствами для этого:Однако я предполагаю, что$properties=@('id','title','slug','url','email','link','subject','level', @{Name='discipline_1';Expression={$_.disciplines[0]}} @{Name='discipline_2';Expression={$_.disciplines[1]}} @{Name='discipline_3';Expression={$_.disciplines[2]}} @{Name='area_1';Expression={$_.areas[0]}} @{Name='area_2';Expression={$_.areas[1]}} ) (Get-Content 'PATH_TO\test.json' -Raw | ConvertFrom-Json)| Select-Object -Property $properties | Export-CSV -NoTypeInformation -Path 'PATH_TO\test.csv'
disciplines
иareas
будут переменной длины для каждой записи. В этом случае вам придется сделать цикл над входом и извлеките наибольшее значение числа как для дисциплин, так и для областей, а затем постройте массив свойств динамически:$inputData = Get-Content 'PATH_TO\test.json' -Raw | ConvertFrom-Json $counts = $inputData | Select-Object -Property @{Name='disciplineCount';Expression={$_.disciplines.Count}},@{Name='areaCount';Expression={$_.areas.count}} $maxDisciplines = $counts | Measure-Object -Maximum -Property disciplineCount | Select-Object -ExpandProperty Maximum $maxAreas = $counts | Measure-Object -Maximum -Property areaCount | Select-Object -ExpandProperty Maximum $properties=@('id','title','slug','url','email','link','subject','level') 1..$maxDisciplines | % { $properties += @{Name="discipline_$_";Expression=[scriptblock]::create("`$_.disciplines[$($_ - 1)]")} } 1..$maxAreas | % { $properties += @{Name="area_$_";Expression=[scriptblock]::create("`$_.areas[$($_ - 1)]")} } $inputData | Select-Object -Property $properties | Export-CSV -NoTypeInformation -Path 'PATH_TO\test.csv'
Этот код не был полностью протестирован, поэтому, возможно, потребуется некоторая настройка, чтобы работать на 100%, но я считаю, что идеи надежны =)
2017-11-20, полностью переписанная функция для улучшения производительности и добавления функций в качестве
-ArrayBase
и поддержки PSStandardMembers и сгруппированных объектов.Сплющить-Объект
Рекурсивно выравнивает объекты, содержащие массивы, хэш-таблицы и (пользовательские) объекты. Все добавленные свойства поставляемых объектов будут выровнены с остальными объектами.Требуется PowerShell версии 2 или выше.
Командлет
Function Flatten-Object { # Version 00.02.12, by iRon [CmdletBinding()]Param ( [Parameter(ValueFromPipeLine = $True)][Object[]]$Objects, [String]$Separator = ".", [ValidateSet("", 0, 1)]$Base = 1, [Int]$Depth = 5, [Int]$Uncut = 1, [String[]]$ToString = ([String], [DateTime], [TimeSpan]), [String[]]$Path = @() ) $PipeLine = $Input | ForEach {$_}; If ($PipeLine) {$Objects = $PipeLine} If (@(Get-PSCallStack)[1].Command -eq $MyInvocation.MyCommand.Name -or @(Get-PSCallStack)[1].Command -eq "<position>") { $Object = @($Objects)[0]; $Iterate = New-Object System.Collections.Specialized.OrderedDictionary If ($ToString | Where {$Object -is $_}) {$Object = $Object.ToString()} ElseIf ($Depth) {$Depth-- If ($Object.GetEnumerator.OverloadDefinitions -match "[\W]IDictionaryEnumerator[\W]") { $Iterate = $Object } ElseIf ($Object.GetEnumerator.OverloadDefinitions -match "[\W]IEnumerator[\W]") { $Object.GetEnumerator() | ForEach -Begin {$i = $Base} {$Iterate.($i) = $_; $i += 1} } Else { $Names = If ($Uncut) {$Uncut--} Else {$Object.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames} If (!$Names) {$Names = $Object.PSObject.Properties | Where {$_.IsGettable} | Select -Expand Name} If ($Names) {$Names | ForEach {$Iterate.$_ = $Object.$_}} } } If (@($Iterate.Keys).Count) { $Iterate.Keys | ForEach { Flatten-Object @(,$Iterate.$_) $Separator $Base $Depth $Uncut $ToString ($Path + $_) } } Else {$Property.(($Path | Where {$_}) -Join $Separator) = $Object} } ElseIf ($Objects -ne $Null) { @($Objects) | ForEach -Begin {$Output = @(); $Names = @()} { New-Variable -Force -Option AllScope -Name Property -Value (New-Object System.Collections.Specialized.OrderedDictionary) Flatten-Object @(,$_) $Separator $Base $Depth $Uncut $ToString $Path $Output += New-Object PSObject -Property $Property $Names += $Output[-1].PSObject.Properties | Select -Expand Name } $Output | Select ([String[]]($Names | Select -Unique)) } }; Set-Alias Flatten Flatten-Object
Синтаксис
<Object[]> Flatten-Object [-Separator <String>] [-Base "" | 0 | 1] [-Depth <Int>] [-Uncut<Int>] [ToString <Type[]>]
Или:
Flatten-Object <Object[]> [[-Separator] <String>] [[-Base] "" | 0 | 1] [[-Depth] <Int>] [[-Uncut] <Int>] [[ToString] <Type[]>]
Параметры
-Object[] <Object[]>
Объект (или объекты), подлежащий выравниванию.
-Separator <String>
(по умолчанию:.
)
Разделитель, используемый между рекурсивными именами свойств. .
-Depth <Int>
(по умолчанию:5
)
Максимальная глубина уплощения рекурсивного свойства. Любое отрицательное значение приведет к неограниченной глубине и может вызвать инфинитивную петлю.
-Uncut <Int>
(по умолчанию:1
)
Число итерацийобъекта , которые оставят неразрезанными дальнейшие свойства объекта, будет ограничено толькоDefaultDisplayPropertySet
. Любое отрицательное значение покажет все свойства всех объектов.
-Base "" | 0 | 1
(по умолчанию:1
)
Первое имя индекса встроенного массива:
1
, массивы будут основываться на 1:<Parent>.1
,<Parent>.2
,<Parent>.3
, ...0
, массивы будут основываться на 0:<Parent>.0
,<Parent>.1
,<Parent>.2
, ...""
, первый элемент в массиве будет безымянным, а за ним последует 1:<Parent>
,<Parent>.1
,<Parent>.2
, ...
-ToString <Type[]= [String], [DateTime], [TimeSpan]>
Список типов значений (default[String], [DateTime], [TimeSpan]
), которые будут преобразованы в string, а не в дальнейшем сглажены. Например, a[DateTime]
может быть уплощен с дополнительными свойствами, такими какDate
,Day
,DayOfWeek
и т.д. но будет преобразовано в одно (String
) свойство вместо.Примечание:
Параметр-Path
предназначен для внутреннего использования, но может использоваться для префикса имен свойств.Примеры
Отвечая на конкретный вопрос:
(Get-Content "PATH_TO\test.json" -Raw | ConvertFrom-Json) | Flatten-Object | Convertto-CSV -NoTypeInformation | Set-Content "PATH_TO\test.csv"
Результат:
{ "url": "http://test.test", "slug": "slug", "id": 10011, "link": "http://test.er", "level": 1, "areas.2": "area_b", "areas.1": "area_a", "disciplines.3": "discipline_c", "disciplines.2": "discipline_b", "disciplines.1": "discipline_a", "subject": "testing", "title": "Test procedure", "email": "test@test.com" }
Стресс-тестирование более сложного пользовательского объекта:
New-Object PSObject @{ String = [String]"Text" Char = [Char]65 Byte = [Byte]66 Int = [Int]67 Long = [Long]68 Null = $Null Booleans = $False, $True Decimal = [Decimal]69 Single = [Single]70 Double = [Double]71 Array = @("One", "Two", @("Three", "Four"), "Five") HashTable = @{city="New York"; currency="Dollar"; postalCode=10021; Etc = @("Three", "Four", "Five")} Object = New-Object PSObject -Property @{Name = "One"; Value = 1; Text = @("First", "1st")} } | Flatten
Результат:
Double : 71 Decimal : 69 Long : 68 Array.1 : One Array.2 : Two Array.3.1 : Three Array.3.2 : Four Array.4 : Five Object.Name : One Object.Value : 1 Object.Text.1 : First Object.Text.2 : 1st Int : 67 Byte : 66 HashTable.postalCode : 10021 HashTable.currency : Dollar HashTable.Etc.1 : Three HashTable.Etc.2 : Four HashTable.Etc.3 : Five HashTable.city : New York Booleans.1 : False Booleans.2 : True String : Text Char : A Single : 70 Null :
Выравнивание сгруппированных объектов:
$csv | Group Name | Flatten | Format-Table
# https://stackoverflow.com/a/47409634/1701026
Выравнивание общих объектов:
(Get-Process)[0] | Flatten-Object
Или a список (массив) объектов:
Get-Service | Flatten-Object -Depth 3 | Export-CSV Service.csv
Обратите внимание, что команда, как показано ниже, занимает несколько часов для вычисления:
Get-Process | Flatten-Object | Export-CSV Process.csv
но почему?Потому что в результате получается таблица с несколькими сотнями строк и несколькими тысячами столбцов. Поэтому, если вы хотите использовать это для выравнивания процесса, лучше ограничить количество строк (с помощью командлета
Where-Object
) или количество столбцов (с помощью командлетаSelect-Object
).последнюю версию
Flatten-Object
см.: https://powersnippets.com/flatten-object/