このページは FormatArc のブログ記事が引用している実測の生データです。検索結果には表示されません (noindex)。
| File | Size |
|---|---|
| README.md | 2.5 KB |
| cases.json | 1.2 KB |
| measure.mjs | 2.2 KB |
| measure.py | 2.4 KB |
| package-lock.json | 1.4 KB |
| package.json | 234 B |
| results-node.json | 2.5 KB |
| results-python.json | 2.4 KB |
| results.json | 6.5 KB |
# YAML parser behaviour differences (YAML 1.1 vs 1.2 ambiguous scalars)
`yaml-json-difference` 記事の「N-parser 実測差分表」の一次ソース。曖昧な YAML スカラー
(Norway problem / on・yes / 8進数 / 60進数 / 素の日付 / 重複キー / merge key 等) を
主要 4 パーサーがどう解決するかを実測する。
## 測定対象パーサー
| パーサー | バージョン | 実行系 | 既定の解決規則 |
| --- | --- | --- | --- |
| js-yaml (`load`, DEFAULT_SCHEMA) | 4.1.1 | Node v26.3.1 | ほぼ YAML 1.2 + timestamp 独自対応 |
| yaml (eemeli, `parse` defaults) | 2.8.3 | Node v26.3.1 | YAML 1.2 core schema |
| PyYAML (`safe_load`) | 6.0.3 | Python 3.14.6 | YAML 1.1 |
| ruamel.yaml (`YAML(typ="safe")`) | 0.19.1 | Python 3.14.6 | YAML 1.2 |
実行環境: Apple M5 Pro / macOS (Darwin 25.5.0)。
## 再現手順
```bash
cd scripts/benchmarks/yaml-parser-differences
# Node 側 (js-yaml + eemeli yaml) → results-node.json
npm install
node measure.mjs
# Python 側 (PyYAML + ruamel.yaml) → results-python.json
python3 -m venv venv
./venv/bin/pip install pyyaml ruamel.yaml
./venv/bin/python measure.py
```
`results.json` は両者を統合した確定値 (テストケース定義は `cases.json`)。
記事本文の表の数値・型はすべて `results.json` と一致させること。
## 実測結果の要点 (2026-07-10 測定)
- `value: NO` を boolean `false` に解決するのは **PyYAML だけ**。js-yaml / eemeli yaml /
ruamel.yaml は文字列 `"NO"` を返す (Norway problem は「YAML の問題」ではなく
「YAML 1.1 系パーサーの問題」)
- `on` / `yes` も同様に PyYAML のみ boolean
- YAML 1.1 の単文字 `y` は 4 パーサーとも文字列 (仕様にあるが実装は追随していない)
- `010` は PyYAML が 8 進数として `8`、他 3 者は 10 進数 `10`
- `0o10` (YAML 1.2 の 8 進表記) は PyYAML だけ文字列 `"0o10"`、他 3 者は `8`
- `1:30` (60 進数) は PyYAML だけ `90`
- 素の日付 `2026-07-10` は eemeli yaml だけ文字列。js-yaml は JS の `Date`、
PyYAML / ruamel.yaml は `datetime.date` に変換する (JSON 変換時の型崩れ要因)
- 重複キーは PyYAML だけ last-wins (`{"a": 2}`)、他 3 者はエラー
- merge key `<<` は eemeli yaml だけ既定で解決しない (`"<<"` という名前のキーとして残る。
`parse(src, { merge: true })` の明示が必要)。他 3 者は既定でマージする
- `.inf` / `~` は 4 パーサーとも一致 (Infinity / null)
{
"topic": "YAML parser behaviour differences (YAML 1.1 vs 1.2 ambiguous scalars)",
"measuredAt": "2026-07-10",
"environment": {
"machine": "Apple M5 Pro (macOS, Darwin 25.5.0)",
"node": "v26.3.1",
"python": "3.14.6"
},
"cases": [
{
"id": "norway-NO",
"yaml": "value: NO",
"question": "YAML 1.1 boolean (Norway problem) or string?"
},
{
"id": "on-keyword",
"yaml": "value: on",
"question": "YAML 1.1 on/off boolean or string?"
},
{
"id": "yes-keyword",
"yaml": "value: yes",
"question": "YAML 1.1 yes/no boolean or string?"
},
{
"id": "single-y",
"yaml": "value: y",
"question": "YAML 1.1 spec lists y/n as boolean — do parsers follow?"
},
{
"id": "octal-legacy",
"yaml": "value: 010",
"question": "YAML 1.1 octal (8) or decimal 10 or string?"
},
{
"id": "octal-12",
"yaml": "value: 0o10",
"question": "YAML 1.2 octal notation (8) or string?"
},
{
"id": "sexagesimal",
"yaml": "value: 1:30",
"question": "YAML 1.1 sexagesimal (90) or string?"
},
{
"id": "bare-date",
"yaml": "value: 2026-07-10",
"question": "timestamp/date object or string?"
},
{
"id": "dot-inf",
"yaml": "value: .inf",
"question": "Infinity or string?"
},
{
"id": "duplicate-keys",
"yaml": "a: 1\na: 2",
"question": "error, last-wins, or first-wins?"
},
{
"id": "merge-key",
"yaml": "base: &b\n x: 1\nchild:\n <<: *b\n y: 2",
"question": "merge key << supported?"
},
{
"id": "tilde-null",
"yaml": "value: ~",
"question": "null or string?"
}
],
"parsers": [
{
"parser": "js-yaml 4.1.1 load (DEFAULT_SCHEMA)",
"results": {
"norway-NO": {
"type": "string",
"repr": "\"NO\""
},
"on-keyword": {
"type": "string",
"repr": "\"on\""
},
"yes-keyword": {
"type": "string",
"repr": "\"yes\""
},
"single-y": {
"type": "string",
"repr": "\"y\""
},
"octal-legacy": {
"type": "number",
"repr": "10"
},
"octal-12": {
"type": "number",
"repr": "8"
},
"sexagesimal": {
"type": "string",
"repr": "\"1:30\""
},
"bare-date": {
"type": "Date",
"repr": "2026-07-10T00:00:00.000Z"
},
"dot-inf": {
"type": "number",
"repr": "Infinity"
},
"duplicate-keys": {
"type": "error",
"repr": "duplicated mapping key (2:1)"
},
"merge-key": {
"type": "object",
"repr": "{\"base\":{\"x\":1},\"child\":{\"x\":1,\"y\":2}}"
},
"tilde-null": {
"type": "null",
"repr": "null"
}
}
},
{
"parser": "yaml (eemeli) 2.8.3 parse (defaults)",
"results": {
"norway-NO": {
"type": "string",
"repr": "\"NO\""
},
"on-keyword": {
"type": "string",
"repr": "\"on\""
},
"yes-keyword": {
"type": "string",
"repr": "\"yes\""
},
"single-y": {
"type": "string",
"repr": "\"y\""
},
"octal-legacy": {
"type": "number",
"repr": "10"
},
"octal-12": {
"type": "number",
"repr": "8"
},
"sexagesimal": {
"type": "string",
"repr": "\"1:30\""
},
"bare-date": {
"type": "string",
"repr": "\"2026-07-10\""
},
"dot-inf": {
"type": "number",
"repr": "Infinity"
},
"duplicate-keys": {
"type": "error",
"repr": "Map keys must be unique at line 2, column 1:"
},
"merge-key": {
"type": "object",
"repr": "{\"base\":{\"x\":1},\"child\":{\"<<\":{\"x\":1},\"y\":2}}"
},
"tilde-null": {
"type": "null",
"repr": "null"
}
}
},
{
"parser": "PyYAML 6.0.3 safe_load",
"results": {
"norway-NO": {
"type": "boolean",
"repr": "false"
},
"on-keyword": {
"type": "boolean",
"repr": "true"
},
"yes-keyword": {
"type": "boolean",
"repr": "true"
},
"single-y": {
"type": "string",
"repr": "\"y\""
},
"octal-legacy": {
"type": "number",
"repr": "8"
},
"octal-12": {
"type": "string",
"repr": "\"0o10\""
},
"sexagesimal": {
"type": "number",
"repr": "90"
},
"bare-date": {
"type": "date",
"repr": "2026-07-10"
},
"dot-inf": {
"type": "number",
"repr": "inf"
},
"duplicate-keys": {
"type": "object",
"repr": "{\"a\": \"2\"}"
},
"merge-key": {
"type": "object",
"repr": "{\"base\": \"{'x': 1}\", \"child\": \"{'x': 1, 'y': 2}\"}"
},
"tilde-null": {
"type": "null",
"repr": "null"
}
}
},
{
"parser": "ruamel.yaml 0.19.1 typ=safe",
"results": {
"norway-NO": {
"type": "string",
"repr": "\"NO\""
},
"on-keyword": {
"type": "string",
"repr": "\"on\""
},
"yes-keyword": {
"type": "string",
"repr": "\"yes\""
},
"single-y": {
"type": "string",
"repr": "\"y\""
},
"octal-legacy": {
"type": "number",
"repr": "10"
},
"octal-12": {
"type": "number",
"repr": "8"
},
"sexagesimal": {
"type": "string",
"repr": "\"1:30\""
},
"bare-date": {
"type": "date",
"repr": "2026-07-10"
},
"dot-inf": {
"type": "number",
"repr": "inf"
},
"duplicate-keys": {
"type": "error",
"repr": "while constructing a mapping"
},
"merge-key": {
"type": "object",
"repr": "{\"base\": \"{'x': 1}\", \"child\": \"{'x': 1, 'y': 2}\"}"
},
"tilde-null": {
"type": "null",
"repr": "null"
}
}
}
]
}