Home > Software design >  Powershell JSON Manipulation
Powershell JSON Manipulation

Time:03-25

$parent = @"
{
    "Property1":  "Property1Value",
    "Description":  "Generic Description",
    "SubProperties":  {
                       
                       "SubSubTemplateProps": {
                                    SubSubSubTemplateProps1 : "SubSubSubTemplateProps1",
                                    SubSubSubTemplateProps2 : {
                                                                  SubSubSubSubTemplateProps1 : [
                                                                        {
                                                                            "Key":  "Name",
                                                                            "Value":  "Temp"
                                                                        },
                                                                        {
                                                                            "Key":  "SupSubSubProp2",
                                                                            "Value":  "Supprop2value"
                                                                        }
                                                                    ]
                                                            }
                            }
                   }
}
"@ | ConvertFrom-Json

$templateobj = $parent.SubProperties.SubSubTemplateProps

$parent.SubProperties.psobject.Properties.Remove("SubSubTemplateProps")


for($j = 0; $j -lt 3; $j  )
{
$i = "{0:D2}" -f ($j 1)
$letters = @("a","b","c")
    foreach($letter in $letters)
    {
        $newobject = $templateobj
        ($newobject.SubSubSubTemplateProps2.SubSubSubSubTemplateProps1 | ? {$_.key -eq "Name"}).Value = "NewValue$letter$i"
        $parent.SubProperties | Add-Member -MemberType NoteProperty -Name "SubSubProp$letter$i" -Value $newobject
    }

}
$parent |convertto-json -Depth 100

Im expecting output like this:

{
    "Property1":  "Property1Value",
    "Description":  "Generic Description",
    "SubProperties":  {
                          "SubSubPropa01":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValuea01"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            },
                          "SubSubPropb01":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValueb01"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            },
                          "SubSubPropa02":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValuea02"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            },
                          "SubSubPropb02":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValueb02"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            }
                      }
}

But I get output like this where all of the noteproperties of the parent are updated instead of just the noteproperty I added. WHAT?!

{
    "Property1":  "Property1Value",
    "Description":  "Generic Description",
    "SubProperties":  {
                          "SubSubPropa01":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValueb02"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            },
                          "SubSubPropb01":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValueb02"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            },
                          "SubSubPropa02":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValueb02"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            },
                          "SubSubPropb02":  {
                                                "SubSubSubTemplateProps1":  "SubSubSubTemplateProps1",
                                                "SubSubSubTemplateProps2":  {
                                                                                "SubSubSubSubTemplateProps1":  [
                                                                                                                   {
                                                                                                                       "Key":  "Name",
                                                                                                                       "Value":  "NewValueb02"
                                                                                                                   },
                                                                                                                   {
                                                                                                                       "Key":  "SupSubSubProp2",
                                                                                                                       "Value":  "Supprop2value"
                                                                                                                   }
                                                                                                               ]
                                                                            }
                                            }
                      }
}

Can someone suggest how to copy a node and replace a sub value then add it back to the parent without it overwriting all other child nodes? Not sure why this is happening.

CodePudding user response:

The fundamental problem, as explained in the comments and in more detail in this related answer is:

  • [pscustomobject] instances, such as returned by ConvertFrom-Json are instances of a .NET reference type, which means that $newobject = $templateobj doesn't create a copy of the object stored in $templateobj in $newobject, it creates a copy of the reference to object $templateobj, so that $templateobj and $newobject end up pointing to the very same object. Only instances of .NET value types are themselves copied with simple assignments - see this answer for background information.
    Therefore, you ended up repeatedly modifying the very same object instead of independent copies of it.

  • While $templateobj.psobject.Copy() is a convenient method to create a shallow clone (copy) of a [pscustomobject] instance, it isn't sufficient in your case, because your instances also contain reference-type instances among the object's property values, which then again get copies as references. In other words: your template object is a [pscustomobject] graph (nested), so it requires deep-cloning, for which there is no built-in method.

    • Note: If you know the structure of the template object ahead of time, there are simpler and more efficient solutions:

      • Use a literal [pscustomobject] object definition inside your loop, which creates a new instance every time - see the related answer.

      • Construct your template as an ordered hashtable ([ordered] @{ ... }), and cast it to [pscustomobject] every time you need a new copy inside the loop - see this answer

      • Define a custom class and instantiate it in every loop iteration - see this answer.


If you do need deep-cloning of [pscustomobject] graphs, use helper function
Copy-PSCustomObject
, defined further below, with the -Deep switch:

$newobject = Copy-PSCustomObject -Deep $templateobj

Helper function Copy-PSCustomObject:

Note: This is not a generic deep-cloning function that works with objects of any type (which would be impossible to implement), but it should work with [pscustomobject] graphs such as returned by ConvertFrom-Json, which limits their composition to:

  • Primitive JSON types that map onto primitive .NET types and [string], all of which either are .NET value types or, in the case of [string] act like them.

  • Nested [pscustomobject] instances with the same composition.

  • Arrays of either.

function Copy-PSCustomObject {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, ValueFromPipeline)]
    [PSCustomObject] $InputObject,
    [switch] $Deep
  )

  begin {
    if ($Deep) {
      # Helper script block (anonymous function) for walking the object graph
      $sb = {
        param($original)
        $copy = $original.psobject.Copy()
        foreach ($prop in $copy.psobject.Properties) {
          if ($prop.Value -is [System.Collections.IEnumerable]) { # Presumed array
            $prop.Value = @($prop.Value) # Clone array
            foreach ($i in 0..($prop.Value.Count-1)) {
              $prop.Value[$i] = & $sb $prop.Value[$i] # Recurse
            }
          }
          elseif ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
            $prop.Value = & $sb $prop.Value # Recurse
          }
          # Otherwise: assume it is a value type or string and needs no cloning.
        }
        # Output the deep-cloned object
        $copy
      }  
    }

  }

  process {
    if ($Deep) {
      & $sb $InputObject
    }
    else {
      # Shallow copy.
      $InputObject.psobject.Copy()
    }
  }
}
  • Related