Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling JSON Reference with ConvertFrom-Json

By default, PowerShell's ConvertFrom-Json cmdlet does not seem to handle references in JSON documents.

{
    "foo": {"$ref": "#/bar"},
    "bar": true
}

I don't really know about the official status of this JSON spec (see here and here), but is there a way to manage such a thing in PowerShell?

like image 329
Sbu Avatar asked Sep 01 '25 03:09

Sbu


1 Answers

AFAIK there are no built-in ways to resolve JSON-references. I usually search for a C# solution for a problem that's not supported in PowerShell as those can be converted to PowerShell-code most of the time, but I couldn't find an easy way to do this in C# without custom code.

So I think you might need to write some functions yourself and use something like Invoke-Expression and reference types (which the converted object is as it is a PSCustomObject) to help you.

Proof of concept (contains many errors):

$json = @'
{
    "foo": {"$ref": "#/bar"},
    "bar": true
}
'@

$t = ConvertFrom-Json -InputObject $json

Write-Host "Before"
$t | Out-Host

function resolveReferences ($obj) {

    #Loop through propeties    
    $obj.psobject.Properties | ForEach-Object {

        #ref-values are PSCustomObjects with a $ref-property, so let's find the PSCustomObjects
        if ($_.TypeNameOfValue -eq 'System.Management.Automation.PSCustomObject') {

            #Verify it was a ref-value by checking for $ref-property
            if ($_.Value.'$ref') { 
                #Convert value to powershell-path like $t.foo
                $refpath = $_.Value.'$ref'.Replace("#/",'$t.').Replace("/",".")

                #Execute generated powershell-path to get the referenced object and replace reference with the referenced object
                $_.Value = (Invoke-Expression -Command $refpath)
            }
        }
    }

}

resolveReferences -obj $t

Write-host "After"
$t | Out-Host

Output:

Before

foo            bar
---            ---
@{$ref=#/bar} True


After

 foo  bar
 ---  ---
True True

Which you can expand to fit your needs. Ex. support for array of objects:

$json = @'
{
    "foo": {"$ref": "#/bar"},
    "fooarray": [
        {"item": {"$ref": "#/bar"}},
        {"item": {"$ref": "#/hello"}}
    ],
    "test": [1,2,3],
    "bar": true,
    "hello": "world"
}
'@

$t = ConvertFrom-Json -InputObject $json

Write-Host "Before"
$t | Out-Host

function resolveReferences ($obj) {

    $obj.psobject.Properties | ForEach-Object {
        if ($_.TypeNameOfValue -eq 'System.Management.Automation.PSCustomObject') {
            if ($_.Value.'$ref') {
                $refpath = $_.Value.'$ref'.Replace("#/",'$t.').Replace("/",".")
                $_.Value = (Invoke-Expression -Command $refpath)
            }
        } elseif ($_.TypeNameOfValue -eq 'System.Object[]') {
            #If array, loop through objects (recursive search)
            $_.Value | ForEach-Object { resolveReferences -obj $_ }
        }
    }

}

resolveReferences -obj $t

Write-host "After"
$t | Out-Host

Output:

#item properties in fooarray-objects are ref paths, it's just PS being to lazy to go deeper and show their values

Before    

foo      : @{$ref=#/bar}
fooarray : {@{item=}, @{item=}}
test     : {1, 2, 3}
bar      : True
hello    : world


After

foo      : True
fooarray : {@{item=True}, @{item=world}}
test     : {1, 2, 3}
bar      : True
hello    : world
like image 125
Frode F. Avatar answered Sep 02 '25 17:09

Frode F.