2

I am making a PSObject from a json file

bar.json

{
  "derp": {
   "buzz": 42 
   },
   "woot":  {
     "toot": 9000
   }
}

I can make a PSCustomObject form the json using ConvertFrom-Json

$foo = Get-Content .\bar.json -Raw |ConvertFrom-Json
$foo.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

However if I try and splat multiple json files, I get an array

$foo = Get-Content .\*.json -Raw |ConvertFrom-Json
$foo.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    Object[]                                 System.Array

In order to iterate over $foo, I need 2 different code paths depending on the object type.

Can I get a single object from multiple json files?
If not, how would I compress an array of objects into a single object?

I've tried to make a new object $bar that contains all the array items of $foo

$bar = new-object psobject
$bar | add-member -name $foo[0].psobject.properties.name -value $foo[0].'derp' -memberType NoteProperty

Update
Per Walter Mitty's request. If I load a single file and run $foo[0]

$foo = Get-Content .\bar.json -Raw |ConvertFrom-Json
$foo[0].gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

$foo[0]

derp                                                    woot
------------                                            ------------
@{Provisioners=System.Object[]; OS=windows; Size=; V... @{Provisioners=System.Object[]; OS=windows; Size=; V...

Solution

I initially implemented AP's answer, but later refactored it to use mklement0 answer.

While $allObjects is an array, it still allows me to reference values by name which is what I was looking for

$allObjects = @(
    Get-ChildItem '.\foo' -Filter *.json -Recurse | Get-Content -Raw | ConvertFrom-Json
)

# iterating over nested objects inside an array is hard.
# make it easier by moving all array objects into 1 parent object where 
# the 'key' is the name (opposed to AP's answer where filename = key)
$newObject = New-Object PSObject
foreach ($i in $allObjects) {
    $i.psobject.members | ?{$_.Membertype -eq 'noteproperty'} |%{$newObject | add-member $_.Name $_.Value}
}
4
  • What would you expect to happen if two json files/objects have the same property with different values? Commented Dec 9, 2016 at 18:16
  • Good point, I would expect an error if any key is not unique Commented Dec 9, 2016 at 18:17
  • 3
    I could be wrong, but I think an array of objects is an object. Commented Dec 9, 2016 at 18:21
  • Try "$foo[0].gettype" or "$foo[0] | gm" to find out more about the first entry in $foo. Commented Dec 9, 2016 at 18:22

2 Answers 2

3

If all you want is an Array of JSON objects which are parsed from the different files:

$final = @{}

# Loop Thru All JSON Files, and add to $Final[<name>] = <JSON>
ls .\*.json | % {
  $o = cat $_ -Raw | ConvertFrom-Json
  $final[$_.name] = [PsCustomObject]$o
}
$final = [PsCustomObject]$final

This will produce a Name -> Data map for all your JSON files as a nested PSObject

Sign up to request clarification or add additional context in comments.

3 Comments

In the linux world, parsing the output of ls is discouraged. Is windows the same?
Nothing in PowerShell is actually returning just strings, they're objects with fields..
@spuder ls and cat in powershell are inly aliase to get-childitem and get-content @AP. I get this error with your code The splatting operator '@' cannot be used to reference variables in an expression.
2

AP.'s helpful answer is an elegant solution for creating a single top-level object ([pscustomobject] instance) that:

  • houses all JSON-converted objects as properties named for their input filenames.

    • E.g., $final would contain a bar.json property whose value is the object equivalent of the JSON string from the question.

    • A caveat is that with duplicate filenames the last file processed would "win".

See bottom for a usage note re Unix-style aliases ls and cat.


To get an array of all converted-from-JSON objects, the following is sufficient:

$allObjects = @(Get-ChildItem -Filter *.json | Get-Content -Raw | ConvertFrom-Json)

Note that if you only need a single directory's JSON files,
$allObjects = @(Get-Content -Path *.json -Raw | ConvertFrom-Json) works too.

Note the use of @(...), the array subexpression operator, which ensures that what is returned is treated as an array, even if only a single object is returned.

  • By default, or when you use $(...), the regular subexpression operator, PowerShell unwraps any (potentially nested) single-item collection and returns only the item itself; see Get-Help about_Operators.

A note on the use of Unix-style aliases ls and cat for PowerShell's Get-ChildItem and Get-Content cmdlets, respectively:

  • Now that PowerShell is cross-platform, these aliases only exist in the Windows edition of PowerShell, whereas a decision was made to omit them from PowerShell Core on Unix platforms, so as not to shadow the standard Unix utilities of the same name.

  • It is better to get into the habit of using PowerShell's own aliases, which follow prescribed naming conventions and do not conflict with platform-specific utilities.

    • E.g., gci can be used for Get-ChildItem, and gc for Get-Content
    • For the naming conventions behind PowerShell's own aliases, see the documentation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.