0

I built a recursive solution to read in a JSON doc and output a flattened table. Now I want to reverse the operation but am having a hard time figuring out a soliton. I need it to be in PowerShell as I just want to add it to my existing module.

I posted about my flatten code here: https://stackoverflow.com/a/63499156/13224569

I have a partial solution, but it falling well short. Here's that code:

$a = '.person.pets[0].color.black.toy.foo' -split '\.'
$b = '.john.harold.cravener' -split '\.'
$z = '.john.is.content' -split '\.'

$m = @{}
$c = @{}
$p = @{}
$pk = $null
$firstTime = $true

foreach($i in $a[1..($a.Count-1)]) {
    $c = @{$i = $null}
    if($firstTime){
        $m = $c
        $firstTime = $false
    }
    
    if($p -and $pk) {
        $p[$pk] = $c
    }
    $p = $c
    $pk = $i
}
$m
exit

And here's its output:

PS D:\> .\tester.ps1 | ConvertTo-Json -Depth 20
{
  "person": {
    "pets[0]": {
      "color": {
        "black": {
          "toy": {
            "foo": null
          }
        }
      }
    }
  }
}

My first challenge, I can't figure out how to move on to the next row and store it to the exiting $m variable. I'm getting tripped up with the way this var is being referenced in PowerShell / .Net Core.

The second challenge is how to deal with arrays, but I haven't even started on this part yet.

Any and all help / suggestions would be greatly appreciated!

jCravener
  • 21
  • 3

1 Answers1

0

After some research, and a lot of work and debugging, I came to a solution. From my testing so far, it works quite well. It's based on simonw's python code here. And while my solution needed to be in PowerShell, it's not a straight port but pretty close. Most of the modifications are based on differences in how PowerShell and Python handles certain things. The main function is ConvertFrom-JhcUtilJsonTable which in turn depends on three helper functions. Here's all of them.

function ConvertFrom-JhcUtilJsonTable {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [System.Collections.Hashtable]
        $jsonHashTable
    )
    begin {}
    
    process {
        foreach ($h in $jsonHashTable) {
            
            $m = @{}
            foreach ($k in $h.Keys) {
                $current = $m
                $val = $h[$k]
                $k = ($k -replace '\]', '') -replace '\[', '.'
                $bits = $k.split('.')
                $path = $bits[1..($bits.Count - 1)]

                $count = 0
    
                foreach ($bit in $path) {
                    $count++
                    if ($v = $current.item($bit)) {
                        $current[$bit] = $v
                    }
                    else {
                        if ($count -eq $path.Count) {
                            $current[$bit] = $val
                        }
                        else {
                            $current[$bit] = @{}
                        }
                    }
                    $current = $current[$bit]
                }
            }
            
            intKeyHashToLists -obj $m
            #--python code had a buit about handling root units e.g. {'$empty': '{}'} - need to add that
        }
    }
    
    end {}
}


#---helper function for ConvertFrom-JhcUtilJsonTable
#
function isType {
    param (
        [Parameter(Mandatory)]
        [System.Object]
        $obj,
        [Parameter(Mandatory)]
        [ValidateSet('Hashtable', 'Object[]')]
        [System.String]
        $typeName
    )
    
    $t = $obj.GetType()

    if ($t.Name -eq $typeName) {
        return $true
    }
    return $false
}

#---helper function for ConvertFrom-JhcUtilJsonTable
#
function allKeysDigits {
    param (
        [Parameter(Mandatory)]
        [System.Collections.Hashtable]
        $h
    )

    foreach ($k in $h.Keys) {
        
        if ($k -match '^0\d') {
            return $false
        }
        
        if ($k -notmatch '^\d+$') {
            return $false  
        }
    }
    return $true
}

#---helper function for ConvertFrom-JhcUtilJsonTable
#
function intKeyHashToLists {
    param (
        [Parameter(Mandatory)]
        [System.Object]
        $obj
    )
    
    if (isType -obj $obj -typeName 'Hashtable') {
        if ($obj -and (allKeysDigits -h $obj)) {
            $a = @()
            foreach ($k in ($obj.Keys | Sort-Object) ) {
                $a += intKeyHashToLists -obj $obj.item($k)
            }

            return ,$a  #--- adding the comma forces this to retun an array even when it's a single element
        }
        else {
            $h = @{}
            foreach ($k in $obj.Keys) {
                $h[$k] = intKeyHashToLists -obj $obj.item($k)
            }
            return $h
        }
    }
    elseif (isType -obj $obj -typeName 'Object[]') {
        return ( $obj | ForEach-Object { intKeyHashToLists -obj $_ } )
    }
    else {
        return $obj
    }
}

jCravener
  • 21
  • 3