From 2393766b8c48ddd6bd5c6421d2abe875d5844377 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 17:52:21 +0000 Subject: [PATCH 1/6] Initial plan From 368a35c010a3afdb61d3e34943419570efbe9003 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:03:39 +0000 Subject: [PATCH 2/6] Implement Import-Json feature with comprehensive tests and examples Co-authored-by: MariusStorhaug <17722253+MariusStorhaug@users.noreply.github.com> --- examples/General.ps1 | 81 ++++++++++++++- src/functions/public/Import-Json.ps1 | 97 ++++++++++++++++++ tests/Json.Tests.ps1 | 148 +++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 src/functions/public/Import-Json.ps1 diff --git a/examples/General.ps1 b/examples/General.ps1 index 3fd4870..c482cf8 100644 --- a/examples/General.ps1 +++ b/examples/General.ps1 @@ -1,5 +1,5 @@ # JSON Module Examples -# This file contains practical examples of using the Format-Json function +# This file contains practical examples of using the Format-Json and Import-Json functions # Import the module (if not already loaded) # Import-Module Json @@ -146,4 +146,81 @@ $formatted #endregion -"`nAll examples completed!" +"#endregion + +#region Import-Json Examples + +# Example 9: Import JSON from a single file +'Example 9: Import JSON from a single file' +# First, create a sample JSON file +$configData = @{ + database = @{ + host = 'localhost' + port = 5432 + name = 'myapp' + ssl = $true + } + logging = @{ + level = 'info' + file = '/var/log/app.log' + } + features = @{ + caching = $true + analytics = $false + } +} +$configFile = '/tmp/config.json' +$configData | ConvertTo-Json -Depth 3 | Set-Content -Path $configFile + +# Import the JSON file +$importedConfig = Import-Json -Path $configFile +$importedConfig +Write-Host "Database host: $($importedConfig.database.host)" +Write-Host "Source file: $($importedConfig._SourceFile)" + +# Example 10: Import multiple JSON files using wildcards +'Example 10: Import multiple JSON files using wildcards' +# Create multiple JSON files +$userData = @{ name = 'Alice'; role = 'admin'; active = $true } +$settingsData = @{ theme = 'dark'; notifications = $true; language = 'en' } + +$userFile = '/tmp/user-data.json' +$settingsFile = '/tmp/user-settings.json' + +$userData | ConvertTo-Json | Set-Content -Path $userFile +$settingsData | ConvertTo-Json | Set-Content -Path $settingsFile + +# Import all user-*.json files +$allUserData = Import-Json -Path '/tmp/user-*.json' +$allUserData | ForEach-Object { + Write-Host "Imported from: $($_._SourceFile)" + $_ | Format-List +} + +# Example 11: Pipeline usage with Import-Json +'Example 11: Pipeline usage with Import-Json' +$jsonFiles = @($configFile, $userFile, $settingsFile) +$allData = $jsonFiles | Import-Json +Write-Host "Imported $($allData.Count) JSON files via pipeline" + +# Example 12: Error handling with Import-Json +'Example 12: Error handling with Import-Json' +try { + Import-Json -Path '/tmp/nonexistent.json' -ErrorAction Stop +} catch { + Write-Warning "Caught expected error: $($_.Exception.Message)" +} + +# Example 13: Combine Import-Json with Format-Json +'Example 13: Combine Import-Json with Format-Json' +$rawConfig = Import-Json -Path $configFile +$formattedConfig = Format-Json -InputObject $rawConfig -IndentationType Spaces -IndentationSize 2 +Write-Host "Formatted imported configuration:" +$formattedConfig + +# Cleanup temporary files +Remove-Item -Path $configFile, $userFile, $settingsFile -ErrorAction SilentlyContinue + +#endregion + +"`nAll examples completed!"" diff --git a/src/functions/public/Import-Json.ps1 b/src/functions/public/Import-Json.ps1 new file mode 100644 index 0000000..7390f85 --- /dev/null +++ b/src/functions/public/Import-Json.ps1 @@ -0,0 +1,97 @@ +function Import-Json { + <# + .SYNOPSIS + Imports JSON data from a file. + + .DESCRIPTION + Reads JSON content from one or more files and converts it to PowerShell objects. + Supports pipeline input for processing multiple files. + + .PARAMETER Path + The path to the JSON file to import. Supports wildcards and multiple paths. + Can be provided via pipeline. + + .PARAMETER Depth + The maximum depth to expand nested objects. Default is 100. + + .EXAMPLE + Import-Json -Path 'config.json' + + Imports JSON data from config.json file. + + .EXAMPLE + Import-Json -Path 'data/*.json' + + Imports JSON data from all .json files in the data directory. + + .EXAMPLE + 'settings.json', 'users.json' | Import-Json + + Imports JSON data from multiple files via pipeline. + + .EXAMPLE + Import-Json -Path 'complex.json' -Depth 50 + + Imports JSON data with a custom maximum depth of 50 levels. + + .LINK + https://psmodule.io/Json/Functions/Import-Json/ + #> + + [CmdletBinding()] + param ( + # Path to the JSON file(s) to import. + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias('FullName')] + [string[]]$Path, + + # Maximum depth for nested object expansion. + [Parameter()] + [int]$Depth = 100 + ) + + process { + foreach ($filePath in $Path) { + try { + # Resolve wildcards and relative paths + $resolvedPaths = Resolve-Path -Path $filePath -ErrorAction Stop + + foreach ($resolvedPath in $resolvedPaths) { + Write-Verbose "Processing file: $($resolvedPath.Path)" + + # Test if the file exists and is a file (not directory) + if (-not (Test-Path -Path $resolvedPath.Path -PathType Leaf)) { + Write-Error "File not found or is not a file: $($resolvedPath.Path)" + continue + } + + # Read file content + $jsonContent = Get-Content -Path $resolvedPath.Path -Raw -ErrorAction Stop + + # Check if file is empty + if ([string]::IsNullOrWhiteSpace($jsonContent)) { + Write-Warning "File is empty or contains only whitespace: $($resolvedPath.Path)" + continue + } + + # Convert JSON to PowerShell object + $jsonObject = $jsonContent | ConvertFrom-Json -Depth $Depth -ErrorAction Stop + + # Add file path information as a note property for reference + if ($jsonObject -is [PSCustomObject]) { + Add-Member -InputObject $jsonObject -MemberType NoteProperty -Name '_SourceFile' -Value $resolvedPath.Path -Force + } + + # Output the object + $jsonObject + } + } catch [System.Management.Automation.ItemNotFoundException] { + Write-Error "Path not found: $filePath" + } catch [System.ArgumentException] { + Write-Error "Invalid JSON format in file: $filePath. $_" + } catch { + Write-Error "Failed to import JSON from file '$filePath': $_" + } + } + } +} \ No newline at end of file diff --git a/tests/Json.Tests.ps1 b/tests/Json.Tests.ps1 index 502da9d..a82f1a1 100644 --- a/tests/Json.Tests.ps1 +++ b/tests/Json.Tests.ps1 @@ -495,4 +495,152 @@ Describe 'Module' { $result | Should -Match '"empty_string":""' } } + + Context 'Import-Json' { + BeforeAll { + # Create test JSON files + $testDataPath = Join-Path $TestDrive 'testdata' + New-Item -Path $testDataPath -ItemType Directory -Force | Out-Null + + $simpleJson = @' +{ + "name": "Test User", + "age": 30, + "active": true +} +'@ + $simpleJsonPath = Join-Path $testDataPath 'simple.json' + $simpleJson | Out-File -FilePath $simpleJsonPath -Encoding UTF8 + + $complexJson = @' +{ + "users": [ + { + "id": 1, + "name": "Alice", + "settings": { + "theme": "dark", + "notifications": true + } + }, + { + "id": 2, + "name": "Bob", + "settings": { + "theme": "light", + "notifications": false + } + } + ], + "metadata": { + "version": "1.0", + "created": "2023-01-01" + } +} +'@ + $complexJsonPath = Join-Path $testDataPath 'complex.json' + $complexJson | Out-File -FilePath $complexJsonPath -Encoding UTF8 + + $emptyJsonPath = Join-Path $testDataPath 'empty.json' + '' | Out-File -FilePath $emptyJsonPath -Encoding UTF8 + + $invalidJsonPath = Join-Path $testDataPath 'invalid.json' + '{ invalid json }' | Out-File -FilePath $invalidJsonPath -Encoding UTF8 + + LogGroup 'Test files created' { + Write-Host "Simple JSON: $simpleJsonPath" + Write-Host "Complex JSON: $complexJsonPath" + Write-Host "Empty JSON: $emptyJsonPath" + Write-Host "Invalid JSON: $invalidJsonPath" + } + } + + It 'Should import simple JSON file' { + $result = Import-Json -Path $simpleJsonPath + LogGroup 'simple import result' { + Write-Host "$($result | ConvertTo-Json -Depth 3 -Compress)" + } + $result.name | Should -Be 'Test User' + $result.age | Should -Be 30 + $result.active | Should -Be $true + $result._SourceFile | Should -Be $simpleJsonPath + } + + It 'Should import complex JSON file' { + $result = Import-Json -Path $complexJsonPath + LogGroup 'complex import result' { + Write-Host "$($result | ConvertTo-Json -Depth 5 -Compress)" + } + $result.users | Should -HaveCount 2 + $result.users[0].name | Should -Be 'Alice' + $result.users[1].name | Should -Be 'Bob' + $result.metadata.version | Should -Be '1.0' + $result._SourceFile | Should -Be $complexJsonPath + } + + It 'Should import multiple files using wildcards' { + # Test with only valid JSON files to avoid interference from invalid ones + $validJsonPath1 = Join-Path $testDataPath 'valid1.json' + $validJsonPath2 = Join-Path $testDataPath 'valid2.json' + + '{"type":"user","name":"Test User"}' | Out-File -FilePath $validJsonPath1 -Encoding UTF8 + '{"type":"product","name":"Widget"}' | Out-File -FilePath $validJsonPath2 -Encoding UTF8 + + $results = Import-Json -Path (Join-Path $testDataPath 'valid*.json') + LogGroup 'wildcard import results' { + Write-Host "Found $($results.Count) results" + $results | ForEach-Object { Write-Host "File: $($_._SourceFile), Type: $($_.type), Name: $($_.name)" } + } + $results | Should -HaveCount 2 + ($results | Where-Object { $_.name -eq 'Test User' }) | Should -Not -BeNullOrEmpty + ($results | Where-Object { $_.name -eq 'Widget' }) | Should -Not -BeNullOrEmpty + } + + It 'Should support pipeline input' { + $results = $simpleJsonPath, $complexJsonPath | Import-Json + LogGroup 'pipeline import results' { + Write-Host "Pipeline results count: $($results.Count)" + } + $results | Should -HaveCount 2 + ($results | Where-Object { $_.name -eq 'Test User' }) | Should -Not -BeNullOrEmpty + ($results | Where-Object { $_.users -ne $null }) | Should -Not -BeNullOrEmpty + } + + It 'Should handle non-existent file gracefully' { + $nonExistentPath = Join-Path $testDataPath 'nonexistent.json' + { Import-Json -Path $nonExistentPath -ErrorAction Stop } | Should -Throw + } + + It 'Should handle invalid JSON gracefully' { + { Import-Json -Path $invalidJsonPath -ErrorAction Stop } | Should -Throw + } + + It 'Should warn on empty files' { + $warningMessages = @() + Import-Json -Path $emptyJsonPath -WarningVariable warningMessages -WarningAction SilentlyContinue + $warningMessages | Should -Not -BeNullOrEmpty + $warningMessages[0] | Should -Match 'empty or contains only whitespace' + } + + It 'Should support custom depth parameter' { + $result = Import-Json -Path $complexJsonPath -Depth 10 + $result.users | Should -HaveCount 2 + $result.metadata | Should -Not -BeNullOrEmpty + } + + It 'Should add source file information' { + $result = Import-Json -Path $simpleJsonPath + $result._SourceFile | Should -Be $simpleJsonPath + } + + It 'Should handle relative paths' { + Push-Location $testDataPath + try { + $result = Import-Json -Path 'simple.json' + $result.name | Should -Be 'Test User' + } finally { + Pop-Location + } + } + } } From 76f4ce636d964b68a4b39573bc97ed87af143f12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:17:01 +0000 Subject: [PATCH 3/6] Move parameter documentation from help to inline comments Co-authored-by: MariusStorhaug <17722253+MariusStorhaug@users.noreply.github.com> --- src/functions/public/Import-Json.ps1 | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/functions/public/Import-Json.ps1 b/src/functions/public/Import-Json.ps1 index 7390f85..ed04861 100644 --- a/src/functions/public/Import-Json.ps1 +++ b/src/functions/public/Import-Json.ps1 @@ -7,13 +7,6 @@ function Import-Json { Reads JSON content from one or more files and converts it to PowerShell objects. Supports pipeline input for processing multiple files. - .PARAMETER Path - The path to the JSON file to import. Supports wildcards and multiple paths. - Can be provided via pipeline. - - .PARAMETER Depth - The maximum depth to expand nested objects. Default is 100. - .EXAMPLE Import-Json -Path 'config.json' @@ -40,12 +33,12 @@ function Import-Json { [CmdletBinding()] param ( - # Path to the JSON file(s) to import. + # The path to the JSON file to import. Supports wildcards and multiple paths. Can be provided via pipeline. [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName')] [string[]]$Path, - # Maximum depth for nested object expansion. + # The maximum depth to expand nested objects. Default is 100. [Parameter()] [int]$Depth = 100 ) From 1ef4e720fec34ac937098b4363fe86d6249730e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:35:43 +0000 Subject: [PATCH 4/6] Update Import-Json Depth parameter to use ConvertFrom-Json default Co-authored-by: MariusStorhaug <17722253+MariusStorhaug@users.noreply.github.com> --- src/functions/public/Import-Json.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/functions/public/Import-Json.ps1 b/src/functions/public/Import-Json.ps1 index ed04861..6894bee 100644 --- a/src/functions/public/Import-Json.ps1 +++ b/src/functions/public/Import-Json.ps1 @@ -38,9 +38,9 @@ function Import-Json { [Alias('FullName')] [string[]]$Path, - # The maximum depth to expand nested objects. Default is 100. + # The maximum depth to expand nested objects. Uses ConvertFrom-Json default if not specified. [Parameter()] - [int]$Depth = 100 + [int]$Depth ) process { @@ -68,7 +68,11 @@ function Import-Json { } # Convert JSON to PowerShell object - $jsonObject = $jsonContent | ConvertFrom-Json -Depth $Depth -ErrorAction Stop + if ($PSBoundParameters.ContainsKey('Depth')) { + $jsonObject = $jsonContent | ConvertFrom-Json -Depth $Depth -ErrorAction Stop + } else { + $jsonObject = $jsonContent | ConvertFrom-Json -ErrorAction Stop + } # Add file path information as a note property for reference if ($jsonObject -is [PSCustomObject]) { From 3e41fbf265eb3f1ba55621e99effe107e368243c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 25 Jul 2025 22:19:31 +0200 Subject: [PATCH 5/6] Refactor Import-Json examples and tests for consistency and clarity --- examples/General.ps1 | 16 ++++++++-------- src/functions/public/Import-Json.ps1 | 20 ++++++++++---------- tests/Json.Tests.ps1 | 4 ++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/General.ps1 b/examples/General.ps1 index c482cf8..33cf797 100644 --- a/examples/General.ps1 +++ b/examples/General.ps1 @@ -146,7 +146,7 @@ $formatted #endregion -"#endregion +#endregion #region Import-Json Examples @@ -155,10 +155,10 @@ $formatted # First, create a sample JSON file $configData = @{ database = @{ - host = 'localhost' - port = 5432 - name = 'myapp' - ssl = $true + host = 'localhost' + port = 5432 + name = 'myapp' + ssl = $true } logging = @{ level = 'info' @@ -175,8 +175,8 @@ $configData | ConvertTo-Json -Depth 3 | Set-Content -Path $configFile # Import the JSON file $importedConfig = Import-Json -Path $configFile $importedConfig -Write-Host "Database host: $($importedConfig.database.host)" -Write-Host "Source file: $($importedConfig._SourceFile)" +Write-Host "Database host: $($importedConfig.database.host)' +Write-Host 'Source file: $($importedConfig._SourceFile)" # Example 10: Import multiple JSON files using wildcards 'Example 10: Import multiple JSON files using wildcards' @@ -215,7 +215,7 @@ try { 'Example 13: Combine Import-Json with Format-Json' $rawConfig = Import-Json -Path $configFile $formattedConfig = Format-Json -InputObject $rawConfig -IndentationType Spaces -IndentationSize 2 -Write-Host "Formatted imported configuration:" +Write-Host 'Formatted imported configuration:' $formattedConfig # Cleanup temporary files diff --git a/src/functions/public/Import-Json.ps1 b/src/functions/public/Import-Json.ps1 index 6894bee..4912bd3 100644 --- a/src/functions/public/Import-Json.ps1 +++ b/src/functions/public/Import-Json.ps1 @@ -9,22 +9,22 @@ function Import-Json { .EXAMPLE Import-Json -Path 'config.json' - + Imports JSON data from config.json file. .EXAMPLE Import-Json -Path 'data/*.json' - + Imports JSON data from all .json files in the data directory. .EXAMPLE 'settings.json', 'users.json' | Import-Json - + Imports JSON data from multiple files via pipeline. .EXAMPLE Import-Json -Path 'complex.json' -Depth 50 - + Imports JSON data with a custom maximum depth of 50 levels. .LINK @@ -48,10 +48,10 @@ function Import-Json { try { # Resolve wildcards and relative paths $resolvedPaths = Resolve-Path -Path $filePath -ErrorAction Stop - + foreach ($resolvedPath in $resolvedPaths) { Write-Verbose "Processing file: $($resolvedPath.Path)" - + # Test if the file exists and is a file (not directory) if (-not (Test-Path -Path $resolvedPath.Path -PathType Leaf)) { Write-Error "File not found or is not a file: $($resolvedPath.Path)" @@ -60,7 +60,7 @@ function Import-Json { # Read file content $jsonContent = Get-Content -Path $resolvedPath.Path -Raw -ErrorAction Stop - + # Check if file is empty if ([string]::IsNullOrWhiteSpace($jsonContent)) { Write-Warning "File is empty or contains only whitespace: $($resolvedPath.Path)" @@ -73,12 +73,12 @@ function Import-Json { } else { $jsonObject = $jsonContent | ConvertFrom-Json -ErrorAction Stop } - + # Add file path information as a note property for reference if ($jsonObject -is [PSCustomObject]) { Add-Member -InputObject $jsonObject -MemberType NoteProperty -Name '_SourceFile' -Value $resolvedPath.Path -Force } - + # Output the object $jsonObject } @@ -91,4 +91,4 @@ function Import-Json { } } } -} \ No newline at end of file +} diff --git a/tests/Json.Tests.ps1 b/tests/Json.Tests.ps1 index a82f1a1..b844634 100644 --- a/tests/Json.Tests.ps1 +++ b/tests/Json.Tests.ps1 @@ -582,10 +582,10 @@ Describe 'Module' { # Test with only valid JSON files to avoid interference from invalid ones $validJsonPath1 = Join-Path $testDataPath 'valid1.json' $validJsonPath2 = Join-Path $testDataPath 'valid2.json' - + '{"type":"user","name":"Test User"}' | Out-File -FilePath $validJsonPath1 -Encoding UTF8 '{"type":"product","name":"Widget"}' | Out-File -FilePath $validJsonPath2 -Encoding UTF8 - + $results = Import-Json -Path (Join-Path $testDataPath 'valid*.json') LogGroup 'wildcard import results' { Write-Host "Found $($results.Count) results" From 87486670e136ffb5548fc65b56be17dcf23022d6 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 25 Jul 2025 22:26:17 +0200 Subject: [PATCH 6/6] Fix string formatting in Import-Json examples for consistency --- examples/General.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/General.ps1 b/examples/General.ps1 index 33cf797..697ee3a 100644 --- a/examples/General.ps1 +++ b/examples/General.ps1 @@ -175,8 +175,8 @@ $configData | ConvertTo-Json -Depth 3 | Set-Content -Path $configFile # Import the JSON file $importedConfig = Import-Json -Path $configFile $importedConfig -Write-Host "Database host: $($importedConfig.database.host)' -Write-Host 'Source file: $($importedConfig._SourceFile)" +"Database host: $($importedConfig.database.host)" +"Source file: $($importedConfig._SourceFile)" # Example 10: Import multiple JSON files using wildcards 'Example 10: Import multiple JSON files using wildcards' @@ -193,7 +193,7 @@ $settingsData | ConvertTo-Json | Set-Content -Path $settingsFile # Import all user-*.json files $allUserData = Import-Json -Path '/tmp/user-*.json' $allUserData | ForEach-Object { - Write-Host "Imported from: $($_._SourceFile)" + "Imported from: $($_._SourceFile)" $_ | Format-List } @@ -201,7 +201,7 @@ $allUserData | ForEach-Object { 'Example 11: Pipeline usage with Import-Json' $jsonFiles = @($configFile, $userFile, $settingsFile) $allData = $jsonFiles | Import-Json -Write-Host "Imported $($allData.Count) JSON files via pipeline" +"Imported $($allData.Count) JSON files via pipeline" # Example 12: Error handling with Import-Json 'Example 12: Error handling with Import-Json' @@ -215,7 +215,7 @@ try { 'Example 13: Combine Import-Json with Format-Json' $rawConfig = Import-Json -Path $configFile $formattedConfig = Format-Json -InputObject $rawConfig -IndentationType Spaces -IndentationSize 2 -Write-Host 'Formatted imported configuration:' +'Formatted imported configuration:' $formattedConfig # Cleanup temporary files @@ -223,4 +223,4 @@ Remove-Item -Path $configFile, $userFile, $settingsFile -ErrorAction SilentlyCon #endregion -"`nAll examples completed!"" +"`nAll examples completed!"