1

I was working on something today and during testing I noticed a very peculiar issue

$arry = @()
$Msg = @{Body="This is a Sample Message";}

$Msg.BrokerProperties=@{}
$Msg.BrokerProperties.Label= "Msg1"

$arry += $Msg
$arry | ConvertTo-Json    # 1st Result

$Msg.BrokerProperties=@{}
$Msg.BrokerProperties.Label= "Msg2"

$arry += $Msg

$arry | ConvertTo-Json

The 1st result of $arry | ConvertTo-Json is as below

{ "Body": "This is a Sample Message", "BrokerProperties": { "Label": "Msg1" } }

The 2nd result of $arry | ConvertTo-Json is as below

[ { "Body": "This is a Sample Message", "BrokerProperties": { "Label": "Msg2" } }, { "Body": "This is a Sample Message", "BrokerProperties": { "Label": "Msg2" } } ]

What I thought would happen is when I set $Msg.BrokerProperties.Label= "Msg2" for 2nd time , then it would only effect the 2nd hashtable in array. but very interestingly that property is getting injected even on to 1st hashtable.

Can someone please explain this behaviour?

I was actually doing this in a loop for prepare a JSON payload for sending on to API call,so I am lookign for a way to update labels for each inner within the json object

3
  • Does changing the array append operation to $arry += ,$Msg change the behaviour? Commented Jun 4, 2015 at 13:29
  • No,I am now starting to realise Arrays are Reference types (not value types) Commented Jun 4, 2015 at 13:34
  • When I try the below line by line the Label property is already in $msg when I output it $arry = @() $Msg = @{Body="This is a Sample Message";BrokerProperties=@{Priority="Medium"}} $Msg1 = $Msg $Prp = $Msg1.Get_Item("BrokerProperties") $Prp.Add("Label","Msg1") $arry += $Msg1 $Msg2 = $Msg $Msg2 | ConvertTo-Json Commented Jun 4, 2015 at 13:37

2 Answers 2

3

Apparently a Powershell array holds only object pointers if a variable is added to it, so you have just added two references to a single object into your arry. To clarify, after your operations this statement:

$arry[0] -eq $arry[1]

will return true. To remedy, you should use clone() function to create an entirely new and independent object from $Msg, so that any modifications will not alter the object you've stored in your array.

$arry = @()
$Msg = @{Body="This is a Sample Message";}

$Msg.BrokerProperties=@{}
$Msg.BrokerProperties.Label= "Msg1"

$arry += $Msg
$arry | ConvertTo-Json    # 1st Result

$Msg=$Msg.clone() # create a new copy
$Msg.BrokerProperties.Label= "Msg2" # alter new copy

$arry += $Msg

$arry | ConvertTo-Json # get two different tables in array

EDIT: In your case, you have to clone BrokerProperties as well, because it is also a hashtable, and cloning an upper level $Msg results in two different objects which contain links to a single nested hashtable. So, in order to receive a completely different object, you have to do a deep copy of your hash table.

$arry = @()
$Msg = @{Body="This is a Sample Message";}

$Msg.BrokerProperties=@{}
$Msg.BrokerProperties.Label= "Msg1"

$arry += $Msg
$arry | ConvertTo-Json    # 1st Result

# $Msg=$Msg.clone() this is not enough!!!
$memStream = new-object IO.MemoryStream
$formatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter
$formatter.Serialize($memStream,$Msg) # serialization makes a string out of an object's structure
$memStream.Position=0
$Msg = $formatter.Deserialize($memStream) # deserialization makes a completely different object
$Msg.BrokerProperties.Label= "Msg2" # and now changing the nested hash table won't change the old one.

$arry += $Msg

$arry | ConvertTo-Json # get two different tables in array

On a side note: If you are creating multiple objects based on the same "template" object, you don't need to serialize all the time, just keep the references to $memStream and $formatter (one memory stream per object to deep copy, one formatter per script) and just call $memstream.position=0; $formatter.deserialize($memstream) to receive another prepared copy of the same object previously serialized.

Sign up to request clarification or add additional context in comments.

1 Comment

Nice deep copy routine
2

Hashtables are passed by reference rather than by value.

To create a new hashtable from an existing hashtable, use Clone():

$arry += [hashtable]$Msg.Clone()

Be warned that this creates a shallow clone, so if you have nested hashtables, the inner most entries will still be reference types, and depending on the circumstances, you'd might want to write you own cloning function

4 Comments

exactly ,what I discovered just now after further digging.Thank you
Ninja'd me by a minute. Oops.
@Vesper That's just how I roll ;-)
@MathiasR.Jessen Still, I've beat you with this issue, as your answer is right only partly, check mine please.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.