diff --git a/examples/General.ps1 b/examples/General.ps1 index 3fd4870..697ee3a 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 +#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 +"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' +# 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 { + "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 +"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 +'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..4912bd3 --- /dev/null +++ b/src/functions/public/Import-Json.ps1 @@ -0,0 +1,94 @@ +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. + + .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 ( + # 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, + + # The maximum depth to expand nested objects. Uses ConvertFrom-Json default if not specified. + [Parameter()] + [int]$Depth + ) + + 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 + 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]) { + 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': $_" + } + } + } +} diff --git a/tests/Json.Tests.ps1 b/tests/Json.Tests.ps1 index 502da9d..b844634 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 + } + } + } }