(PowerShell) PSCustomObject のピリオド(ドット)入りのキー名にアクセスする

Powershell で JSON から読み込んだオブジェクトにピリオド(ドット)入りのキー名があり、これにどうやってアクセスしようか悩みました。

現象

{
    "user": {
        "user.name": "testuser",
        "user.email": "email@example.com"
    }
}

例えばこんな config.json があるとします。

この JSONファイル を読み込み、ユーザの入力を受け付けて設定を上書きするスクリプトを書きました。

# JSONファイル 読み込み
[String]$configPath = Join-Path ( Convert-Path . ) 'config.json'
$configData = Get-Content -Path $configPath -Raw -Encoding UTF8 | ConvertFrom-JSON

# 一度出力
Write-Host $configData.user['user.name']

# 入力受付
[String]$username = Read-Host 'ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。'
if (-not ($username -match "^[a-zA-z0-9\-_]+$") ) {
    Write-Host 'ERROR: ユーザ名 に使用できない文字が含まれています。' -BackgroundColor DarkRed
    Write-Host `r`n

    exit
}
$configData.user['user.name'] = $username

# 再度出力
Write-Host $configData.user['user.name']

ここで問題となったのが user.name という . (ピリオド(ドット))入りのキー名。オブジェクトで . でキー名を連結できるのが JavaScript に似ていたので、つい JavaScript のように $configData.user['user.name'] と書いてしましました。

すると

> PowerShell -ExecutionPolicy RemoteSigned .\test.ps1

ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。: aaa
型 System.Management.Automation.PSObject のオブジェクトにインデックスを付けることはできません。
発生場所 PATH\TO\PROJECT\test.ps1:16 文字:1
+ $configData.user['user.name'] = $username
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : CannotIndex

怒られました。しかも、初出の位置ではなく変数で上書きする部分で。初出の Write-Host による標準出力はブランクを出力してスルーされたようです。

以上より、

  • 変数の値を読み込む場合(出力など): 存在しないキーとしてブランク出力してそのままスルー
  • 変数の値を書き込む場合: エラー

となるようです。困った。

対処

プロパティ名に文字列を使用しても、やはり機能します。

$myObject.'Name'

なん……だと……。

ということで上記スクリプトを書き換えます。

# JSONファイル 読み込み
[String]$configPath = Join-Path ( Convert-Path . ) 'config.json'
$configData = Get-Content -Path $configPath -Raw -Encoding UTF8 | ConvertFrom-JSON

# 一度出力
Write-Host $configData.user.'user.name'

# 入力受付
[String]$username = Read-Host 'ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。'
if (-not ($username -match "^[a-zA-z0-9\-_]+$") ) {
    Write-Host 'ERROR: ユーザ名 に使用できない文字が含まれています。' -BackgroundColor DarkRed
    Write-Host `r`n

    exit
}
$configData.user.'user.name' = $username

# 再度出力
Write-Host $configData.user.'user.name'

肝は $configData.user.'user.name'$configData.user['user.name'] の角括弧 ([])を外してキー名の文字列をそのまま . (ピリオド(ドット)) で繋げました。

> PowerShell -ExecutionPolicy RemoteSigned .\test.ps1
testuser
ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。: abc
abc

上手く行きました。

参考

この記事を書いた人

アバター

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。