PowerShellでYAMLを扱う

# 概要

PowerShellにはYAMLを読み書きする標準ライブラリが現時点でないが、標準ライブラリ以外であればいくつかこれが可能なライブラリは存在している。そのうちの一つである[powershell-yaml](https://github.com/cloudbase/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`を扱うためのものはない。

```powershell
PS> Get-Command -Noun YAML
PS> 
```

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

* [ConvertFrom-Yaml, ConvertTo-Yaml · Issue #3607 · PowerShell/PowerShell · GitHub](https://github.com/PowerShell/PowerShell/issues/3607)

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

```powershell
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](http://dbadailystuff.com/a-brief-introduction-to-yaml-in-powershell)」に辿りついた。2018年のブログエントリで、コンパクトにこうした概況をまとめたうえで、以下の2モジュールについて説明している。

* [PSYaml: A Powershell module to convert YAML documents to and from PowerShell objects](https://github.com/Phil-Factor/PSYaml)
* [powershell-yaml: PowerShell CmdLets for YAML format manipulation](https://github.com/cloudbase/powershell-yaml)

パッと見て気付いたことがある。リポジトリの更新日時は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のみを登録している。

```PowerShell
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` でインストールされたこととバージョンの確認もしておく。

```PowerShell
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にあったサンプルデータを使うことにする。

```PowerShell
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文字列に変換し、結果を確認する。

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

変換できている。

<div style="page-break-before:always" />

## キー順序を維持したYAMLへの変換（OrderedDictionaryのConvertTo-Yaml）

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

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

```PowerShell
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文字列に変換し、結果を確認する。

```PowerShell
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オブジェクトに変換させることにする。

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

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

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

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

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

```PowerShell
PS> $data | Get-Member

   TypeName: System.Collections.Hashtable

Name              MemberType            Definition
----              ----------            ----------
(略)
```

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

## キー順序を維持したYAMLからの変換（ConvertFrom-Yaml -Ordered）

「[Josep - A Brief introduction to YAML in Powershell](http://dbadailystuff.com/a-brief-introduction-to-yaml-in-powershell)」の中で `-Ordered` オプションが紹介されていたので、試してみる。対象は同じYAML文字列とする。

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

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

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

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

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

```PowerShell
PS> $data | Get-Member

   TypeName: System.Collections.Specialized.OrderedDictionary

Name              MemberType            Definition
----              ----------            ----------
(略)
```

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

# 参考

* [PowershellでYAMLを扱う（Import-YAML） - Qiita](https://qiita.com/asterisk9101/items/63e0017dd00dec854b01)
* [ConvertFrom-Yaml, ConvertTo-Yaml · Issue #3607 · PowerShell/PowerShell · GitHub](https://github.com/PowerShell/PowerShell/issues/3607)
* [Josep - A Brief introduction to YAML in Powershell](http://dbadailystuff.com/a-brief-introduction-to-yaml-in-powershell)
* [powershell-yaml: PowerShell CmdLets for YAML format manipulation](https://github.com/cloudbase/powershell-yaml)