Задать значение свойства вложенного объекта по имени в PowerShell

Я хочу установить значение свойства вложенного объекта с помощью PowerShell. Когда вы пытаетесь установить значение свойств первого уровня, все просто:

$propertyName = "someProperty"
$obj.$propertyName = "someValue"  # ← It works

Для вложенных свойств это не работает:

$propertyName = "someProperty.someNestedProperty"
$obj.$propertyName = "someValue"  # ← It doesn't work and raises an error.

Как установить значение свойства вложенного объекта по имени свойства с помощью PowerShell?

MCVE

Для тех, кто хочет воспроизвести проблему, вот простой пример:

$Obj= ConvertFrom-Json '{ "A": "x", "B": {"C": "y"} }'
# Or simply create the object:
# $Obj= @{ A = "x"; B = @{C = "y"} }
$Key = "B.C"
$Value = "Some Value"
$Obj.$Key = $Value

Запустите команду, и вы получите ошибку:

"Свойство" BC "не может быть найдено для этого объекта. Убедитесь, что свойство существует и может быть установлено".

3 ответа

Решение

Я создал SetValue а также GetValue методы, позволяющие динамически получать и устанавливать вложенное свойство объекта (включая объект json) по имени, и они работают отлично!

Это рекурсивные методы, которые разрешают сложное свойство и шаг за шагом получают вложенное свойство, разделяя имя вложенного свойства.

GetValue и SetValue вложенных свойств по имени

# Methods
function GetValue($object, $key)
{
    $p1,$p2 = $key.Split(".")
    if($p2) { return GetValue -object $object.$p1 -key $p2 }
    else { return $object.$p1 }
}
function SetValue($object, $key, $Value)
{
    $p1,$p2 = $key.Split(".")
    if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
    else { $object.$p1 = $Value }
}

пример

В следующем примере я установил B.C динамически используя SetValue и получить его значение по имени, используя GetValue метод:

# Example
$Obj = ConvertFrom-Json '{ "A": "x", "B": {"C": "y"} }'
# Or simply create the object:
# $Obj = @{ A = "x"; B = @{C = "y"} }
$Key = "B.C"
$Value = "Changed Dynamically!"
SetValue -object $Obj -key $Key -Value $Value
GetValue -object $Obj -key $Key

Ваши собственные решения эффективны, но не поддерживают индексированный доступ как часть вложенного пути доступа к свойствам (например, B[1].C)

Простой альтернативой является использование Invoke-Expression ( iex) . Хотя, как правило, этого следует избегать , есть исключительные случаи, когда он предлагает простейшее решение, и это одно из них:

Предполагая, что вы полностью контролируете или неявно доверяете строке доступа к свойству:

      $obj = ConvertFrom-Json '{ "A": "x", "B": [ {"C": "y"}, { "C": "z"} ] }'

$propPath = 'B[1].C'

# GET
Invoke-Expression "`$obj.$propPath" # -> 'z'

# SET
$value = 'Some Value'
Invoke-Expression "`$obj.$propPath = `$value" 

Если вы не доверяете вводу , вы можете избежать нежелательной инъекции команд следующим образом:

      $safePropPath = $propPath -replace '(`)*\$', '$1$1`$$'
Invoke-Expression "`$obj.$safePropPath"
# ...

Информацию об удобной функции / методе ETS, который безопасно упаковывает вышеперечисленные функции , см. В этом ответе .

Могу я предложить обновление решения Резы. С помощью этого решения вы можете иметь много уровней вложенных свойств.

function GetValue($object, [string[]]$keys)
{
    $propertyName = $keys[0]
    if($keys.count.Equals(1)){
        return $object.$propertyName
    }
    else { 
        return GetValue -object $object.$propertyName -key ($keys | Select-Object -Skip 1)
    }
}


function SetValue($object, [string[]]$keys, $value)
{
    $propertyName = $keys[0]
    if($keys.count.Equals(1)) {
        $object.$propertyName = $value
    }
    else { 
        SetValue -object $object.$propertyName -key ($keys | Select-Object -Skip 1) -value $value
    }
}

Применение

$Obj = ConvertFrom-Json '{ "A": "x", "B": {"C": {"D" : "y"}} }'
SetValue $Obj -key "B.C.D".Split(".") -value "z"
GetValue $Obj -key "B.C.D".Split(".")
Другие вопросы по тегам