2

I'm looking for a better solution to the following problem. This code:

$a = "{value:'1'}" | ConvertFrom-Json
$b = $null
Write-Output ("a: {0} - b: {1}" -f $a, $b)
$b = $a
Write-Output ("a: {0} - b: {1}" -f $a, $b)
$a.value = '2'
Write-Output ("a: {0} - b: {1}" -f $a, $b)
$b.value = '3'
Write-Output ("a: {0} - b: {1}" -f $a, $b)

Returns this:

a: @{value=1} - b: 
a: @{value=1} - b: @{value=1}
a: @{value=2} - b: @{value=2}
a: @{value=3} - b: @{value=3}

What I need is this: after setting $b to $a I should be able to change $a while $b remains unchanged.

The only solution I could come up with is do a convert to and from JSON, like this:

$a = "{value:'1'}" | ConvertFrom-Json
$b = $null
Write-Output ("a: {0} - b: {1}" -f $a, $b)
$b = $a | ConvertTo-Json -depth 100 | ConvertFrom-Json
Write-Output ("a: {0} - b: {1}" -f $a, $b)
$a.value = '2'
Write-Output ("a: {0} - b: {1}" -f $a, $b)
$b.value = '3'
Write-Output ("a: {0} - b: {1}" -f $a, $b)

That returns my desired result:

a: @{value=1} - b: 
a: @{value=1} - b: @{value=1}
a: @{value=2} - b: @{value=1}
a: @{value=2} - b: @{value=3}

I'm looking for a better solution because in my real-world example the variables are large JSON files that are loaded from disk, updated and saved via a PUT to an API.

Update: PSObject.Copy() does not solve my real-world problem because that JSON has nested objects (as Ansgar Wiechers predicted in his answer).

See this example for the difference in behaviour between ConvertTo/From-Json and PSObject.Copy():

$a = "{value:'1',nested:{foo:'original value'}}" | ConvertFrom-Json
$b = $a | ConvertTo-Json -depth 100 | ConvertFrom-Json
$a.nested.foo = 'changing $a does not change $b'
$b | ConvertTo-Json -Depth 1
$c = $a.PSObject.Copy()
$a.nested.foo = 'changing $a changes $c!'
$c | ConvertTo-Json -Depth 1

Result:

{
    "value":  "1",
    "nested":  {
                   "foo":  "original value"
               }
}
{
    "value":  "1",
    "nested":  {
                   "foo":  "changing $a changes $c!"
               }
}

Update 2: the code in the other question that my question is marked as a duplicate of does indeed work in my case (see below), but I'll stick to my ConvertTo/From-Json because it's a lot simpler...

$a = "{value:'1',nested:{foo:'original value'}}" | ConvertFrom-Json

$ms = New-Object System.IO.MemoryStream
$bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
$bf.Serialize($ms, $a)
$ms.Position = 0
$b = $bf.Deserialize($ms)
$ms.Close()

$a.nested.foo = 'changing $a does not change $b'
$b | ConvertTo-Json -Depth 1

Result:

{
    "value":  "1",
    "nested":  {
                   "foo":  "original value"
               }
}
Frank van Eykelen
  • 1,926
  • 1
  • 23
  • 31
  • 1
    Deserialize the JSON to immutable objects that support efficient change-on-copy by keeping references to their parent immutable objects. PowerShell offers no such facility, nor is it easy to create. There are, of course, various questions and answers on SO about cloning `PSCustomObject` instances, but none of those would avoid the central inefficiency of copying large objects. The trick is to *not* copy them. (That said, unless your JSON files are in the gigabytes, this inefficiency is unlikely to matter in practice. Make sure you're not prematurely optimizing.) – Jeroen Mostert Jul 28 '17 at 12:20
  • See here: https://stackoverflow.com/questions/9581568/how-to-create-new-clone-instance-of-psobject-object – arco444 Jul 28 '17 at 12:20

0 Answers0