Skip to content

Commit 540a100

Browse files
🚀[Feature]: Add Import-Json function (#7)
This pull request introduces the `Import-Json` function to the module, enabling users to import JSON data from files into PowerShell objects. It also includes comprehensive examples and tests to demonstrate its functionality and ensure reliability. - Fixes #4 ### New Functionality: `Import-Json` * **`src/functions/public/Import-Json.ps1`**: Added the `Import-Json` function, which supports importing JSON data from single or multiple files, handling pipeline input, custom depth levels, and error scenarios. It also annotates imported objects with the source file path for traceability. ### Examples Update * **`examples/General.ps1`**: Expanded the examples to include detailed use cases for `Import-Json`, such as importing JSON from single and multiple files, pipeline usage, error handling, and combining with `Format-Json`. ### Documentation Improvement * **`examples/General.ps1`**: Updated the file header to reflect the addition of `Import-Json` examples. ### Comprehensive Testing * **`tests/Json.Tests.ps1`**: Added a new test context for `Import-Json`, covering various scenarios such as importing valid and invalid JSON files, handling empty files, using wildcards, pipeline input, custom depth levels, and relative paths. These tests ensure robustness and reliability. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MariusStorhaug <17722253+MariusStorhaug@users.noreply.github.com> Co-authored-by: Marius Storhaug <marstor@hotmail.com>
1 parent 19b6030 commit 540a100

3 files changed

Lines changed: 320 additions & 1 deletion

File tree

examples/General.ps1

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# JSON Module Examples
2-
# This file contains practical examples of using the Format-Json function
2+
# This file contains practical examples of using the Format-Json and Import-Json functions
33

44
# Import the module (if not already loaded)
55
# Import-Module Json
@@ -146,4 +146,81 @@ $formatted
146146

147147
#endregion
148148

149+
#endregion
150+
151+
#region Import-Json Examples
152+
153+
# Example 9: Import JSON from a single file
154+
'Example 9: Import JSON from a single file'
155+
# First, create a sample JSON file
156+
$configData = @{
157+
database = @{
158+
host = 'localhost'
159+
port = 5432
160+
name = 'myapp'
161+
ssl = $true
162+
}
163+
logging = @{
164+
level = 'info'
165+
file = '/var/log/app.log'
166+
}
167+
features = @{
168+
caching = $true
169+
analytics = $false
170+
}
171+
}
172+
$configFile = '/tmp/config.json'
173+
$configData | ConvertTo-Json -Depth 3 | Set-Content -Path $configFile
174+
175+
# Import the JSON file
176+
$importedConfig = Import-Json -Path $configFile
177+
$importedConfig
178+
"Database host: $($importedConfig.database.host)"
179+
"Source file: $($importedConfig._SourceFile)"
180+
181+
# Example 10: Import multiple JSON files using wildcards
182+
'Example 10: Import multiple JSON files using wildcards'
183+
# Create multiple JSON files
184+
$userData = @{ name = 'Alice'; role = 'admin'; active = $true }
185+
$settingsData = @{ theme = 'dark'; notifications = $true; language = 'en' }
186+
187+
$userFile = '/tmp/user-data.json'
188+
$settingsFile = '/tmp/user-settings.json'
189+
190+
$userData | ConvertTo-Json | Set-Content -Path $userFile
191+
$settingsData | ConvertTo-Json | Set-Content -Path $settingsFile
192+
193+
# Import all user-*.json files
194+
$allUserData = Import-Json -Path '/tmp/user-*.json'
195+
$allUserData | ForEach-Object {
196+
"Imported from: $($_._SourceFile)"
197+
$_ | Format-List
198+
}
199+
200+
# Example 11: Pipeline usage with Import-Json
201+
'Example 11: Pipeline usage with Import-Json'
202+
$jsonFiles = @($configFile, $userFile, $settingsFile)
203+
$allData = $jsonFiles | Import-Json
204+
"Imported $($allData.Count) JSON files via pipeline"
205+
206+
# Example 12: Error handling with Import-Json
207+
'Example 12: Error handling with Import-Json'
208+
try {
209+
Import-Json -Path '/tmp/nonexistent.json' -ErrorAction Stop
210+
} catch {
211+
Write-Warning "Caught expected error: $($_.Exception.Message)"
212+
}
213+
214+
# Example 13: Combine Import-Json with Format-Json
215+
'Example 13: Combine Import-Json with Format-Json'
216+
$rawConfig = Import-Json -Path $configFile
217+
$formattedConfig = Format-Json -InputObject $rawConfig -IndentationType Spaces -IndentationSize 2
218+
'Formatted imported configuration:'
219+
$formattedConfig
220+
221+
# Cleanup temporary files
222+
Remove-Item -Path $configFile, $userFile, $settingsFile -ErrorAction SilentlyContinue
223+
224+
#endregion
225+
149226
"`nAll examples completed!"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
function Import-Json {
2+
<#
3+
.SYNOPSIS
4+
Imports JSON data from a file.
5+
6+
.DESCRIPTION
7+
Reads JSON content from one or more files and converts it to PowerShell objects.
8+
Supports pipeline input for processing multiple files.
9+
10+
.EXAMPLE
11+
Import-Json -Path 'config.json'
12+
13+
Imports JSON data from config.json file.
14+
15+
.EXAMPLE
16+
Import-Json -Path 'data/*.json'
17+
18+
Imports JSON data from all .json files in the data directory.
19+
20+
.EXAMPLE
21+
'settings.json', 'users.json' | Import-Json
22+
23+
Imports JSON data from multiple files via pipeline.
24+
25+
.EXAMPLE
26+
Import-Json -Path 'complex.json' -Depth 50
27+
28+
Imports JSON data with a custom maximum depth of 50 levels.
29+
30+
.LINK
31+
https://psmodule.io/Json/Functions/Import-Json/
32+
#>
33+
34+
[CmdletBinding()]
35+
param (
36+
# The path to the JSON file to import. Supports wildcards and multiple paths. Can be provided via pipeline.
37+
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
38+
[Alias('FullName')]
39+
[string[]]$Path,
40+
41+
# The maximum depth to expand nested objects. Uses ConvertFrom-Json default if not specified.
42+
[Parameter()]
43+
[int]$Depth
44+
)
45+
46+
process {
47+
foreach ($filePath in $Path) {
48+
try {
49+
# Resolve wildcards and relative paths
50+
$resolvedPaths = Resolve-Path -Path $filePath -ErrorAction Stop
51+
52+
foreach ($resolvedPath in $resolvedPaths) {
53+
Write-Verbose "Processing file: $($resolvedPath.Path)"
54+
55+
# Test if the file exists and is a file (not directory)
56+
if (-not (Test-Path -Path $resolvedPath.Path -PathType Leaf)) {
57+
Write-Error "File not found or is not a file: $($resolvedPath.Path)"
58+
continue
59+
}
60+
61+
# Read file content
62+
$jsonContent = Get-Content -Path $resolvedPath.Path -Raw -ErrorAction Stop
63+
64+
# Check if file is empty
65+
if ([string]::IsNullOrWhiteSpace($jsonContent)) {
66+
Write-Warning "File is empty or contains only whitespace: $($resolvedPath.Path)"
67+
continue
68+
}
69+
70+
# Convert JSON to PowerShell object
71+
if ($PSBoundParameters.ContainsKey('Depth')) {
72+
$jsonObject = $jsonContent | ConvertFrom-Json -Depth $Depth -ErrorAction Stop
73+
} else {
74+
$jsonObject = $jsonContent | ConvertFrom-Json -ErrorAction Stop
75+
}
76+
77+
# Add file path information as a note property for reference
78+
if ($jsonObject -is [PSCustomObject]) {
79+
Add-Member -InputObject $jsonObject -MemberType NoteProperty -Name '_SourceFile' -Value $resolvedPath.Path -Force
80+
}
81+
82+
# Output the object
83+
$jsonObject
84+
}
85+
} catch [System.Management.Automation.ItemNotFoundException] {
86+
Write-Error "Path not found: $filePath"
87+
} catch [System.ArgumentException] {
88+
Write-Error "Invalid JSON format in file: $filePath. $_"
89+
} catch {
90+
Write-Error "Failed to import JSON from file '$filePath': $_"
91+
}
92+
}
93+
}
94+
}

tests/Json.Tests.ps1

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,4 +495,152 @@ Describe 'Module' {
495495
$result | Should -Match '"empty_string":""'
496496
}
497497
}
498+
499+
Context 'Import-Json' {
500+
BeforeAll {
501+
# Create test JSON files
502+
$testDataPath = Join-Path $TestDrive 'testdata'
503+
New-Item -Path $testDataPath -ItemType Directory -Force | Out-Null
504+
505+
$simpleJson = @'
506+
{
507+
"name": "Test User",
508+
"age": 30,
509+
"active": true
510+
}
511+
'@
512+
$simpleJsonPath = Join-Path $testDataPath 'simple.json'
513+
$simpleJson | Out-File -FilePath $simpleJsonPath -Encoding UTF8
514+
515+
$complexJson = @'
516+
{
517+
"users": [
518+
{
519+
"id": 1,
520+
"name": "Alice",
521+
"settings": {
522+
"theme": "dark",
523+
"notifications": true
524+
}
525+
},
526+
{
527+
"id": 2,
528+
"name": "Bob",
529+
"settings": {
530+
"theme": "light",
531+
"notifications": false
532+
}
533+
}
534+
],
535+
"metadata": {
536+
"version": "1.0",
537+
"created": "2023-01-01"
538+
}
539+
}
540+
'@
541+
$complexJsonPath = Join-Path $testDataPath 'complex.json'
542+
$complexJson | Out-File -FilePath $complexJsonPath -Encoding UTF8
543+
544+
$emptyJsonPath = Join-Path $testDataPath 'empty.json'
545+
'' | Out-File -FilePath $emptyJsonPath -Encoding UTF8
546+
547+
$invalidJsonPath = Join-Path $testDataPath 'invalid.json'
548+
'{ invalid json }' | Out-File -FilePath $invalidJsonPath -Encoding UTF8
549+
550+
LogGroup 'Test files created' {
551+
Write-Host "Simple JSON: $simpleJsonPath"
552+
Write-Host "Complex JSON: $complexJsonPath"
553+
Write-Host "Empty JSON: $emptyJsonPath"
554+
Write-Host "Invalid JSON: $invalidJsonPath"
555+
}
556+
}
557+
558+
It 'Should import simple JSON file' {
559+
$result = Import-Json -Path $simpleJsonPath
560+
LogGroup 'simple import result' {
561+
Write-Host "$($result | ConvertTo-Json -Depth 3 -Compress)"
562+
}
563+
$result.name | Should -Be 'Test User'
564+
$result.age | Should -Be 30
565+
$result.active | Should -Be $true
566+
$result._SourceFile | Should -Be $simpleJsonPath
567+
}
568+
569+
It 'Should import complex JSON file' {
570+
$result = Import-Json -Path $complexJsonPath
571+
LogGroup 'complex import result' {
572+
Write-Host "$($result | ConvertTo-Json -Depth 5 -Compress)"
573+
}
574+
$result.users | Should -HaveCount 2
575+
$result.users[0].name | Should -Be 'Alice'
576+
$result.users[1].name | Should -Be 'Bob'
577+
$result.metadata.version | Should -Be '1.0'
578+
$result._SourceFile | Should -Be $complexJsonPath
579+
}
580+
581+
It 'Should import multiple files using wildcards' {
582+
# Test with only valid JSON files to avoid interference from invalid ones
583+
$validJsonPath1 = Join-Path $testDataPath 'valid1.json'
584+
$validJsonPath2 = Join-Path $testDataPath 'valid2.json'
585+
586+
'{"type":"user","name":"Test User"}' | Out-File -FilePath $validJsonPath1 -Encoding UTF8
587+
'{"type":"product","name":"Widget"}' | Out-File -FilePath $validJsonPath2 -Encoding UTF8
588+
589+
$results = Import-Json -Path (Join-Path $testDataPath 'valid*.json')
590+
LogGroup 'wildcard import results' {
591+
Write-Host "Found $($results.Count) results"
592+
$results | ForEach-Object { Write-Host "File: $($_._SourceFile), Type: $($_.type), Name: $($_.name)" }
593+
}
594+
$results | Should -HaveCount 2
595+
($results | Where-Object { $_.name -eq 'Test User' }) | Should -Not -BeNullOrEmpty
596+
($results | Where-Object { $_.name -eq 'Widget' }) | Should -Not -BeNullOrEmpty
597+
}
598+
599+
It 'Should support pipeline input' {
600+
$results = $simpleJsonPath, $complexJsonPath | Import-Json
601+
LogGroup 'pipeline import results' {
602+
Write-Host "Pipeline results count: $($results.Count)"
603+
}
604+
$results | Should -HaveCount 2
605+
($results | Where-Object { $_.name -eq 'Test User' }) | Should -Not -BeNullOrEmpty
606+
($results | Where-Object { $_.users -ne $null }) | Should -Not -BeNullOrEmpty
607+
}
608+
609+
It 'Should handle non-existent file gracefully' {
610+
$nonExistentPath = Join-Path $testDataPath 'nonexistent.json'
611+
{ Import-Json -Path $nonExistentPath -ErrorAction Stop } | Should -Throw
612+
}
613+
614+
It 'Should handle invalid JSON gracefully' {
615+
{ Import-Json -Path $invalidJsonPath -ErrorAction Stop } | Should -Throw
616+
}
617+
618+
It 'Should warn on empty files' {
619+
$warningMessages = @()
620+
Import-Json -Path $emptyJsonPath -WarningVariable warningMessages -WarningAction SilentlyContinue
621+
$warningMessages | Should -Not -BeNullOrEmpty
622+
$warningMessages[0] | Should -Match 'empty or contains only whitespace'
623+
}
624+
625+
It 'Should support custom depth parameter' {
626+
$result = Import-Json -Path $complexJsonPath -Depth 10
627+
$result.users | Should -HaveCount 2
628+
$result.metadata | Should -Not -BeNullOrEmpty
629+
}
630+
631+
It 'Should add source file information' {
632+
$result = Import-Json -Path $simpleJsonPath
633+
$result._SourceFile | Should -Be $simpleJsonPath
634+
}
635+
636+
It 'Should handle relative paths' {
637+
Push-Location $testDataPath
638+
try {
639+
$result = Import-Json -Path 'simple.json'
640+
$result.name | Should -Be 'Test User'
641+
} finally {
642+
Pop-Location
643+
}
644+
}
645+
}
498646
}

0 commit comments

Comments
 (0)