Yml icons created by Darius Dan - Flaticon
TIG 所属の多賀です。
表題の通り、Go で map 型の YAML 出力の際、key を指定した順序にする方法を調査・実装してみました。
TL;DR
- map の key は YAML 変換ライブラリ側でソートされた上で、出力することで順序が固定化されている
- 指定した順序で出力したいので、map を struct へ変換して出力した
背景
Go の map のソート順は不定であることは、よく言われることかなと思います。
(言語仕様にも明記されています。)
Map types
A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. The value of an uninitialized map is nil.
そのため、map をソートして出力したい場合は、 map に含まれる key のリストをソートし、ソートされた key ごとに map の value を出力することで実現します。
例: The Go Playground - map sort sample
import ( |
同様に、map を YAML へ出力する際も key でソートして出力したかったのですが、YAML を扱うライブラリ側でソート順が固定化されており、できませんでした。ライブラリの調査について以下に記載します。
まず、Go で YAML を扱うためには、一般的に以下ライブラリが利用できます。
(今回のサンプルは、 go-yaml/yaml.v3 を利用しています。)
map を YAML 形式へ出力するコードを以下の通りに実装してみました。
例: The Go Playground - map to yaml sample
import ( |
出力を見てみると(もしくは、PlayGround上で複数回実行していみると) 固定でアルファベット順にソートされて出力されていることがわかります。
ソースコードを読んでみると、ライブラリ内で key をソートした上で出力するように実装されていました。
それぞれのライブラリの該当行は以下になります。
(key がソートされてないと出力ごとに余計な差分が出て不便なので、ライブラリ側で吸収してくれているのかと思いました。)
ライブラリ側で固定でソート順が定められている以上、map の出力を指定のソート順にできないことになります。
今回、 指定のソート順にしたい要望があり、どうにかできないか調査・実装してみました。
対応方法
やりたいことは、 「map の YAML 出力時の key を指定した順序で出力すること」になります。
上記記載の通り、map のソート順はライブラリ側で固定化されているので、map 型のままだと難しそうです。
map 型の他に、key/value 形式でソート順が固定されているデータ構造としては、 struct が該当すると考え、map → struct の変換をすれば良いのではと思いつきました。
ですが、map は任意の key/value 値になるため、コンパイル前に struct を定義することはできません。
そのため、map の key/value 値を元にして、実行時に struct を生成することにしました。
また、YAML 形式へ変換する実装を map → struct への変換処理にカスタマイズしたいです。
変換処理を独自カスタマイズするには、go-yaml/yaml.v3 の場合は Marshaler
interface を実装することで可能です。
yaml/yaml.go at v3 · go-yaml/yaml · GitHub
// The Marshaler interface may be implemented by types to customize their |
(goccy/go-yaml
の場合も同様の interface (InterfaceMarshaler) でカスタマイズ可能な模様です。)
整理すると、以下の 2点を実装する必要があります。
(1) map の値から実行時に struct を新たに生成し、struct のフィールドを指定したソート順で定義する。
(2) YAML 出力時に map → struct 変換を実装するため、出力カスタマイズ可能な interface を満たすように実装する。
こちらの 2点を満たす実装を以下の通り実施してみました。
(※ reflection が多用されたナイーブな実装なので、本運用等のコードに使うのは少しリスキーだと思います。今回は CLI ツールでの利用であったため、問題ないとしています。)
The Go Playground - sort map to yaml sample
import ( |
まとめ
Go で map の YAML 出力時のソート順を指定する方法を実装してみました。
YAML 形式に閉じずに、他の形式でも似た実装で同じような結果が得られそうです。
実装方法としては、reflection 利用のあまり良くないコードかなとは思いつつ、他に方法も浮かばなかったのが実際のところです。
(他に良い実現方法があれば、ぜひ知りたいです。)
最後まで読んでいただきありがとうございました。