🦟
y座標のキーだけ "y" になる不可解な挙動とYAML仕様
環境
RUBY_VERSION # => "3.2.2"
require "yaml"
YAML # => Psych
YAML::VERSION # => "5.1.1"
事の発端
YAML.dump("x" => 1, "y" => 2, "z" => 3) # => "---\nx: 1\n\"y\": 2\nz: 3\n"
なぜ y だけダブルクォーテーションで囲まれた "y"
なのだろう?
コードを読む
なんか特別扱いされてた。n もいるし。y はまさかの yes (true) として認識されかけていたようだ。つまり y を y と書いてしまうと、YAML側からしたら真偽値の真と区別がつかなくなってしまうため "y" としたらしい。
ということはもし YAML で "y": "y"
と書くべきところを y: y
と書いてしまうと true: true
と解釈されてしまうということだ。
実際に確認してみると、
YAML.load("---\ny: y\n") # => {"y"=>"y"}
そうならんのんかい!
じゃあ y を "y"
にした意図はなんだったんだろうか。
バージョンの確認
YAML.dump("", header: true) # => "%YAML 1.1\n--- ''\n"
1.1 とのこと。
常識的に考えたら YAML_SPEC_VERSION
のような定数を用意してくれていてもよさそうだが、コード内にはマジックナンバー 1.1 が散乱していたので、こんな方法で確認するしかなかった。
なお、
Psych::LIBYAML_VERSION # => "0.2.5"
というのもあったがこれじゃない。
1.1 と 1.2 の違い
項目 | YAML 1.1 | YAML 1.2 |
---|---|---|
発行年 | 2005 | 2009 |
JSONとの互換性 | なし | あり |
文字列の扱い | 矛盾する場合が多く、解析が難しい | 明確であり、解析が容易 |
データ構造 | 一部のデータ構造が複雑 | より単純で直感的 |
空白の扱い | 曖昧 | より厳格 |
ようするに 1.1 はめちゃくちゃらしい。
1.2 仕様の YAML ライブラリはない?
探したけど Ruby には見つからなかった。ChatGPT に聞くと Psych を紹介してくるけど、それは違う。
まとめ
y は、まさかの true のことで、y をそのまま書き出してしまうと、次に読み込んだとき true になってしまうので "y" としたのだろうと考えたが、実際には、 y は true ではなく "y" として読み取られたので、結局何がなんだかわけがわからない。
XML の代替として期待されて登場した YAML だが 1.1 の仕様はめちゃくちゃだった。そこで 1.2 の仕様ができたけど残念ながら Ruby の標準ライブラリはいまだに 1.1 仕様のままだった。
参照
Discussion