Как клонировать переменную PowerShell PSCustomObject, чтобы отключить ее от другой переменной?

Я ищу лучшее решение следующей проблемы. Этот код:

$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)

Возвращает это:

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

Мне нужно следующее: после установки $b на $a я должен иметь возможность изменить $a, в то время как $b остается неизменным.

Единственное решение, которое я мог придумать, это преобразовать в JSON и из него, например:

$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)

Это возвращает мой желаемый результат:

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

Я ищу лучшее решение, потому что в моем реальном примере переменные представляют собой большие файлы JSON, которые загружаются с диска, обновляются и сохраняются через PUT в API.

Обновление: PSObject.Copy() не решает мою реальную проблему, потому что этот JSON имеет вложенные объекты (как предсказывал Ансгар Вихерс в своем ответе).

В этом примере показана разница в поведении между ConvertTo/From-Json и 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

Результат:

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

Обновление 2: код в другом вопросе, который помечен как дубликат, действительно работает в моем случае (см. ниже), но я буду придерживаться своего ConvertTo/From-Json, потому что он намного проще...

$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

Результат:

{
    "value":  "1",
    "nested":  {
                   "foo":  "original value"
               }
}

person Frank van Eykelen    schedule 28.07.2017    source источник
comment
Десериализуйте JSON в неизменяемые объекты, которые поддерживают эффективное изменение при копировании, сохраняя ссылки на их родительские неизменяемые объекты. PowerShell не предлагает такой возможности, и ее не так просто создать. Конечно, в SO есть различные вопросы и ответы о клонировании экземпляров PSCustomObject, но ни один из них не позволит избежать центральной неэффективности копирования больших объектов. Хитрость заключается в том, чтобы не копировать их. (Тем не менее, если ваши файлы JSON не имеют гигабайт, эта неэффективность вряд ли будет иметь значение на практике. Убедитесь, что вы не оптимизируете преждевременно.)   -  person Jeroen Mostert    schedule 28.07.2017
comment
См. здесь: stackoverflow.com/questions/9581568/   -  person arco444    schedule 28.07.2017