我今天正在研究一些事情,在测试过程中我发现了一个非常特殊的问题
$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
$arry | ConvertTo-Json
的第一个结果如下
{ “身体”:“这是一个示例消息”, “BrokerProperties”:{ “标签”:“Msg1” }}
$arry | ConvertTo-Json
的第二个结果如下
[ { “身体”:“这是一个示例消息”, “BrokerProperties”:{ “标签”:“Msg2” } }, { “身体”:“这是一个示例消息”, “BrokerProperties”:{ “标签”:“Msg2” } }]
我认为会发生的事情是当我第二次设置$Msg.BrokerProperties.Label= "Msg2"
时,它只会影响数组中的第二个哈希表。但非常有趣的是,该属性甚至被注入到第一个哈希表中。
有人可以解释一下这种行为吗?
我实际上是在循环中执行此操作以准备发送到API调用的JSON有效负载,因此我期待一种方法来更新json对象中每个内部的标签
答案 0 :(得分:3)
显然,如果向其添加变量,Powershell数组只保存对象指针,因此您只需将对单个对象的两个引用添加到arry
中。为了澄清,在你的操作之后这句话:
$arry[0] -eq $arry[1]
将返回true。要解决此问题,您应该使用clone()
函数从$Msg
创建一个全新的独立对象,这样任何修改都不会改变您存储在数组中的对象。
$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
编辑:在您的情况下,您还必须克隆BrokerProperties
,因为它也是哈希表,克隆上层$Msg
会导致两个不同的对象其中包含指向单个嵌套哈希表的链接。因此,为了接收完全不同的对象,您必须执行哈希表的deep copy。
$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
旁注:如果要基于相同的"模板创建多个对象"对象,您不需要一直序列化,只需保留对$memStream
和$formatter
的引用(每个对象一个内存流进行深层复制,每个脚本使用一个格式化程序),然后调用{ {1}}接收先前序列化的同一对象的另一个准备副本。
答案 1 :(得分:2)
Hashtables通过引用而不是按值传递。
要从现有哈希表创建新哈希表,请使用Clone()
:
$arry += [hashtable]$Msg.Clone()
请注意,这会创建一个浅层克隆,所以如果你有嵌套的哈希表,那么最里面的条目仍然是引用类型,并且根据具体情况,你可能想要编写自己的克隆函数