Home > front end >  Copying Powershell PSCustomObject to array element will pass reference, not value
Copying Powershell PSCustomObject to array element will pass reference, not value

Time:12-31

Not sure what I am doing wrong, but when copying a PSCustomObject in Powershell to an array element, this is copied by reference. See this:

$body = [PSCustomObject]@{ 
    albumId = $album_id;
    newMediaItems = [PSCustomObject]@()    
} 

$mediaItem = [PSCustomObject]@{
    description = "";
    simpleMediaItem = [PSCustomObject]@{ 
        fileName = "";
        uploadToken = "";
        }
}

$mediaItem.description = "Friend of Mickey Mouse"
$mediaItem.simpleMediaItem.fileName = "Goofy.txt"
$mediaItem.simpleMediaItem.uploadToken = "1111"

$body.newMediaItems  = $mediaItem.PsObject.Copy()

$mediaItem.description = "Friend of Rocker Duck"
$mediaItem.simpleMediaItem.fileName = "Donald Duck.txt"
$mediaItem.simpleMediaItem.uploadToken = "2222"

$body.newMediaItems  =  $mediaItem.PsObject.Copy()


$body | ConvertTo-Json -depth 4

Output:

{
  "albumId": null,
  "newMediaItems": [
    {
      "description": "Friend of Mickey Mouse",
      "simpleMediaItem": {
        "fileName": "Donald Duck.txt",
        "uploadToken": "2222"
      }
    },
    {
      "description": "Friend of Rocker Duck",
      "simpleMediaItem": {
        "fileName": "Donald Duck.txt",
        "uploadToken": "2222"
      }
    }
  ]
} 

CodePudding user response:

.psobject.Copy() performs shallow (member-wise) cloning of [pscustomobject] instances.

Since your simpleMediaItem property contains a reference to a .NET reference type (which happens to be another [pscustomobject] instance), it is the reference that is copied, so that both the original [pscustomobject] and the clone obtained via .psobject.Copy() reference the very same object.

For deep (recursive) cloning (for which there is no universal solution), you'd have to create a custom implementation.

You can avoid the problem altogether using a custom class definition:

class MediaItem {
  [string] $description
  [pscustomobject] $simpleMediaItem = [pscustomobject] @{ 
      fileName = ""
      uploadToken = ""
  }
}

Now you can simply use [MediaItem]::new() every time you want a new, independent instance.

To put it all together:

class MediaItem {
  [string] $description
  [pscustomobject] $simpleMediaItem = [pscustomobject] @{ 
    fileName    = ""
    uploadToken = ""
  }
}

$body = [PSCustomObject]@{ 
  albumId       = $album_id;
  newMediaItems = @()    
} 

# Create a new instance.
$mediaItem = [MediaItem]::new()
$mediaItem.description = "Friend of Mickey Mouse"
$mediaItem.simpleMediaItem.fileName = "Goofy.txt"
$mediaItem.simpleMediaItem.uploadToken = "1111"

$body.newMediaItems  = $mediaItem

# Create a new instance.
$mediaItem = [MediaItem]::new()
$mediaItem.description = "Friend of Rocker Duck"
$mediaItem.simpleMediaItem.fileName = "Donald Duck.txt"
$mediaItem.simpleMediaItem.uploadToken = "2222"

$body.newMediaItems  = $mediaItem

$body | ConvertTo-Json -Depth 4  

CodePudding user response:

What you're seeing is expected, you're only copying the base object. I would also recommend you doing this with function or a class, it would be much easier and no copying needed.

$body = [PSCustomObject]@{ 
    albumId = $album_id;
    newMediaItems = [PSCustomObject]@()    
} 
$mediaItem = [PSCustomObject]@{
    description = "";
    simpleMediaItem = [PSCustomObject]@{ 
        fileName = "";
        uploadToken = "";
    }
}
$mediaItem.description = "Friend of Mickey Mouse"
$mediaItem.simpleMediaItem.fileName = "Goofy.txt"
$mediaItem.simpleMediaItem.uploadToken = "1111"

$body.newMediaItems  = $mediaItem

$base = $mediaItem.PsObject.Copy()
$child = $mediaItem.simpleMediaItem.PSObject.Copy()

$base.description = "Friend of Rocker Duck"
$child.fileName = "Donald Duck.txt"
$child.uploadToken = "2222"
$base.simpleMediaItem = $child

$body.newMediaItems  = $base
  • Related