Let's say I want to pull some XML from a website:
PS C:\WINDOWS\system32> $content = Invoke-RestMethod -Uri https://www.bankofcanada.ca/valet/observations/FXCADUSD/xml -Method Get
And let's say I want to peel off some of the subelements and fool around with them, like I would with any other object:
PS C:\WINDOWS\system32> $test = $content.data.seriesDetail.series
PS C:\WINDOWS\system32> $test
id label description dimension
-- ----- ----------- ---------
FXCADUSD CAD/USD Canadian dollar to US dollar daily exchange rate dimension
PS C:\WINDOWS\system32> $test.id = "12345678"
PS C:\WINDOWS\system32> $test
id label description dimension
-- ----- ----------- ---------
12345678 CAD/USD Canadian dollar to US dollar daily exchange rate dimension
Why is it that the parent document ends up being affected?
PS C:\WINDOWS\system32> $content.data.seriesDetail.series
id label description dimension
-- ----- ----------- ---------
12345678 CAD/USD Canadian dollar to US dollar daily exchange rate dimension
PS C:\WINDOWS\system32>
Is this intended behavior?
Not so much explicitly _intended_ by PowerShell, but more a function of how reference types behave.
A majority of .NET types are reference types, which means that if you assign the value to another variable, only the reference to that item is assigned. This avoids what would otherwise be a significant amount of processing overhead that would be required to entirely copy the objects, and essentially means that if you assign a reference to a variable, it will still affect the original variable or the original object it was attached to.
To cover the few cases where you _do_ want to create actual copies of items, many classes have Clone() methods which can be applied to create copies, though the degree to which they are copied and how much data can be duplicated will vary depending on the implementation for that individual class.
According to the documentation here there is a .CloneNode() method on XmlElement, so you should be able to do $test = $content.data.seriesDetail.series.CloneNode($true) to create a copy of the node to work with and modify without affecting the original node. The $true boolean tells the method to copy all child nodes as well; if you instead pass $false as the argument, only the target node is cloned; all child nodes would remain references to the originals.
This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.
Most helpful comment
Not so much explicitly _intended_ by PowerShell, but more a function of how reference types behave.
A majority of .NET types are reference types, which means that if you assign the value to another variable, only the reference to that item is assigned. This avoids what would otherwise be a significant amount of processing overhead that would be required to entirely copy the objects, and essentially means that if you assign a reference to a variable, it will still affect the original variable or the original object it was attached to.
To cover the few cases where you _do_ want to create actual copies of items, many classes have
Clone()methods which can be applied to create copies, though the degree to which they are copied and how much data can be duplicated will vary depending on the implementation for that individual class.According to the documentation here there is a
.CloneNode()method on XmlElement, so you should be able to do$test = $content.data.seriesDetail.series.CloneNode($true)to create a copy of the node to work with and modify without affecting the original node. The$trueboolean tells the method to copy all child nodes as well; if you instead pass$falseas the argument, only the target node is cloned; all child nodes would remain references to the originals.