PowerShellでYAMLを扱う


概要

PowerShellにはYAMLを読み書きする標準ライブラリが現時点でないが、標準ライブラリ以外であればいくつかこれが可能なライブラリは存在している。そのうちの一つであるpowershell-yamlモジュールを使い、PowerShell 7.1.3上で以下ができることを確認した。

  • ConvertTo-YAML $Hashtable
    PowerShellオブジェクトからYAML文字列に変換する。
  • ConvertTo-YAML $OrderedDictionary
    PowerShellオブジェクトからYAML文字列に変換する。変換対象変数が OrderedDictionary 型の場合、キー順序は維持される。
  • ConvertFrom-YAML $String
    YAML文字列からPowerShellオブジェクトに変換する。
  • ConvertFrom-YAML $String -Ordered
    YAML文字列からPowerShellオブジェクトに変換する。 -Ordered が指定された場合、キー順序は維持される(返り値が OrderedDictionary 型になる)。

詳細

PowerShellのYAML対応状況

PowerShellの標準モジュールでは、現在のところYAMLを扱うためのものはない。

PS> Get-Command -Noun YAML
PS> 

GitHubのPowerShellリポジトリでは2017年からいまだにこの点の議論がされている(つまり標準モジュールがないまま要望が続けられている)ようだ。

ユーザーによる実装は複数あり、Google検索するとまず「PowershellでYAMLを扱う(Import-YAML) - Qiita」が出てきたりもするが、YAMLの読み込み(Import-YAML)だけでなく対になるYAMLを生成するコマンドレットも欲しい。Find-Module すると次のように複数のモジュールが見つかる。

PS> Find-Module -Name "*YAML*"

Version  Name              Repository  Description
-------  ----              ----------  -----------
0.4.2    powershell-yaml   PSGallery   Powershell module for serializing and d...
1.0.3    FXPSYaml          PSGallery   PowerShell module used to intrepret Yam...
0.2.0    Gainz-Yaml        PSGallery   Gainz: Yaml
0.1.0    Gz-Yaml           PSGallery   # Gz-Yaml…
1.0.0    Pode.Yaml         PSGallery   A YAML Request and Response parser exte...
1.0      PSYamlQuery       PSGallery   PowerShell wrapper for yq.exe, enables ...
1.4      WSDLToYAML        PSGallery   Parse WSDL, extract the model and gener...

上記のGitHubイシューからリンクをたどっていって「Josep - A Brief introduction to YAML in Powershell」に辿りついた。2018年のブログエントリで、コンパクトにこうした概況をまとめたうえで、以下の2モジュールについて説明している。

パッと見て気付いたことがある。リポジトリの更新日時はPSYamlが2019年9月11日、powershell-yamlは2020年5月5日で後者の方がより最近まで更新されている。一方でpowershell-yamlはREADMEの冒頭にYamlDotNetのラッパーだと書かれており、.NET版のWindows PowerShellではなく現在のPowerShell Coreで動くかちょっと不安だ。

This powershell module is a thin wrapper on top of YamlDotNet that serializes and un-serializes simple powershell objects to and from YAML.

でもそういえば、PowerShell 7.1.3の手元の環境でもFind-Moduleに出てきた。まずはpowershell-yamlが動作するか、手を動かして確認することに決める。

powershell-yamlのインストール

まずあらためて環境について確認しておくと、Windows上のPowerShell 7.1.3。リポジトリとしてはPSGelleryのみを登録している。

PS> $PSVersionTable | select PSVersion, PSEdition, OS

PSVersion PSEdition OS
--------- --------- --
7.1.3     Core      Microsoft Windows 10.0.19041

PS> Get-PSRepository

Name        InstallationPolicy   SourceLocation
----        ------------------   --------------
PSGallery   Trusted              https://www.powershellgallery.com/api/v2

インストールは通常通り Install-Module powershell-yaml でよい。やらなくてもいいけど、一応 Get-Module でインストールされたこととバージョンの確認もしておく。

PS> Install-Module powershell-yaml
PS> Get-Module powershell-yaml

ModuleType Version PreRelease Name              ExportedCommands
---------- ------- ---------- ----              ----------------
Script     0.4.2              powershell-yaml   {ConvertFrom-Yaml, ConvertTo-Yaml, cfy, cty}

YAMLへの変換(ConvertTo-Yaml)

まずYAML文字列への変換。READMEにあったサンプルデータを使うことにする。

PS> $data = @{"hello"="world"; "anArray"=@(1,2,3); "nested"=@{"array"=@("this", "is", "an", "array")}}
PS> $data

Name                           Value
----                           -----
hello                          world
nested                         {array}
anArray                        {1, 2, 3}

YAML文字列に変換し、結果を確認する。

PS> $yaml = ConvertTo-Yaml $data
PS> $yaml
hello: world
nested:
  array:
  - this
  - is
  - an
  - array
anArray:
- 1
- 2
- 3

変換できている。

キー順序を維持したYAMLへの変換(OrderedDictionaryのConvertTo-Yaml)

先の例では $data には hello anArray nested の順にキーを作成したはずだけど、YAML文字列ではこの順序が変わっている。ただそれ以前に $data を表示した時にすでに変わっていた。 $data をキーの順序を保持する OrderedDictionary にした場合の動作も確認してみた。

サンプルデータを OrderedDictionary として作成する。

PS> $data = [ordered]@{"hello"="world"; "anArray"=@(1,2,3); "nested"=@{"array"=@("this", "is", "an", "array")}}
PS> $data

Name                           Value
----                           -----
hello                          world
anArray                        {1, 2, 3}
nested                         {array}

YAML文字列に変換し、結果を確認する。

PS> $yaml = ConvertTo-Yaml $data
PS> $yaml
hello: world
anArray:
- 1
- 2
- 3
nested:
  array:
  - this
  - is
  - an
  - array

OrderdDictionaryのキー順序と一致したYAML文字列が得られてる。

YAMLからの変換(ConvertFrom-Yaml)

先ほどのYAML文字列を対象として、PowerShellオブジェクトに変換させることにする。

PS> $yaml
hello: world
anArray:
- 1
- 2
- 3
nested:
  array:
  - this
  - is
  - an
  - array

PowerShellオブジェクトに変換し、結果を確認する。

PS> $data = ConvertFrom-Yaml $yaml
PS> $data

Name                           Value
----                           -----
hello                          world
nested                         {array}
anArray                        {1, 2, 3}

変換できていた。ただしキーの順序は保持されてない。型も確認しておく。

PS> $data | Get-Member

   TypeName: System.Collections.Hashtable

Name              MemberType            Definition
----              ----------            ----------
()

返り値は Hashtable 型になっていた。

キー順序を維持したYAMLからの変換(ConvertFrom-Yaml -Ordered)

Josep - A Brief introduction to YAML in Powershell」の中で -Ordered オプションが紹介されていたので、試してみる。対象は同じYAML文字列とする。

PS> $yaml
hello: world
anArray:
- 1
- 2
- 3
nested:
  array:
  - this
  - is
  - an
  - array

PowerShellオブジェクトに変換し、結果を確認する。

PS> $data = ConvertFrom-Yaml $yaml -Ordered
PS> $data

Name                           Value
----                           -----
hello                          world
anArray                        {1, 2, 3}
nested                         {array}

キーの順序を維持して変換できていた。型も確認しておく。

PS> $data | Get-Member

   TypeName: System.Collections.Specialized.OrderedDictionary

Name              MemberType            Definition
----              ----------            ----------
()

返り値は OrderedDictionary 型になっていた。

参考