json-comments-parser-errors

このページは FormatArc のブログ記事が引用している実測の生データです。検索結果には表示されません (noindex)。

Files

FileSize
README.md5.1 KB
cases.json981 B
measure.mjs6.4 KB
package-lock.json1001 B
package.json244 B
python-runner.py771 B
results.json6.2 KB

README.md

# JSON comments acceptance and error messages (5 parsers, measured)

`json-comments` 記事が引用するパーサー別エラーメッセージ・行番号の一次ソース。
`//` 行コメントと `/* */` ブロックコメントを含む JSON を主要 5 パーサーに実際に食わせ、
**受理するかどうか・実際に出るエラーメッセージ文字列・報告される行/列/offset** を記録する。

## 測定対象パーサー

| パーサー | 呼び方 | バージョン |
| --- | --- | --- |
| V8 (`JSON.parse`) | Node.js 組み込み | Node 実行系のバージョンに追従 (measure.mjs 内で `process.version` を記録) |
| Python (`json.loads`) | `python3 python-runner.py` をサブプロセスで呼ぶ | `python3 --version` を記録 |
| `jq` | インストールされていれば `jq .` にケースを標準入力で渡す | `jq --version` を記録。無ければ `available: false` |
| JSON5 (`json5` npm package) | `JSON5.parse` | package.json 固定 (2.2.3) |
| JSONC (`jsonc-parser` npm package, VS Code が使うパーサー) | `jsonc.parse` + `errors` 配列 | package.json 固定 (3.3.1) |

V8 は FormatArc の json-formatter ツールが実際に使っているパーサー (`JSON.parse` / `JSON.stringify`)。

## ケース (cases.json)

1. `line-comment-object` — 複数行オブジェクトの最初のプロパティ前にある行コメント + 値の後のブロックコメント (記事本文が示すスニペットそのもの)
2. `line-comment-only` — 最初のプロパティ前の行コメントのみ (ブロックコメント無し)
3. `block-comment-after-value` — 1 行の値の後にあるブロックコメント
4. `block-comment-before-value` — 値が来るべき位置にブロックコメントが立っているケース
5. `valid-baseline` — コメント無しの同じオブジェクト。全パーサーが受理することを確認する陰性対照

## `accepted` の定義

- V8 / Python / jq / JSON5 は「例外を投げずにパースが完了したか」で判定する。
- `jsonc-parser` の `parse()` はエラー回復 (lenient recovery) を行う実装だが、コメントは
  そもそも文法として許容されるため、本ベンチの 5 ケースでは常に例外を投げず `errors` 配列も
  空になる。`accepted = (errors.length === 0)` の定義自体は json-trailing-comma ベンチと揃えて
  あり、コメントを含まない別入力を追加検証する場合はこの定義に従うこと。

## 再現手順

```bash
cd scripts/benchmarks/json-comments-parser-errors
npm install
node measure.mjs   # Python 側は python-runner.py をサブプロセスで自動的に呼ぶ
```

`results.json` が上書き生成される。標準出力にも同じ JSON が出る。

## 測定のスコープと限界

- **バージョン依存**: 各パーサーの挙動・エラーメッセージ文言はバージョンで変わりうる。
  `results.json` の `environment` に記録した実測時のバージョンでのみ保証する
  (measure.mjs 実行時点で固定されている `json5` / `jsonc-parser` を除き、V8 / Python / jq
  は実行系にインストールされているものをそのまま使う)。
- **jq 未インストール環境**: `environment.jqAvailable` が `false` になり、各ケースの
  jq 側エントリは `{ "available": false, "accepted": null, "message": null, "position": null }`
  で記録される。推測で埋めない。
- **jq のエラーメッセージはコメント特有の言い回しにならない**: jq はコメントを認識せず、
  コメント記号 (`//` や `/*`) をトークンとして解析しようとして `Invalid numeric literal` などの
  無関係な文言を出す (measure.mjs 実行時の実測値どおり)。記事本文で引用する際、jq のエラー文言が
  「コメントが原因」だと直接読み取れない点に注意する。
- **VS Code の `Comments are not permitted in JSON.` は対象外**: この文言は VS Code の言語
  サービス (エディタの UI 上で表示される診断メッセージ) が出すものであり、コマンドラインの
  `jsonc-parser` ライブラリ単体を呼んでも再現しない。本ベンチマークはコマンドラインから実行できる
  5 パーサーのみを対象とし、VS Code エディタの UI 文言は測定対象に含めない。
- **位置情報の形式が揃わない**: V8 は `position` (offset) と `line/column` の両方または
  片方のみを返す (ケースによって message の書式自体が変わる。`block-comment-before-value`
  のように position が丸ごと `null` になるケースもある)。jq / Python は line/column、
  jsonc-parser は offset のみで line/column を返さない。フォーマット横断の「行番号」比較には
  JSON5/V8/Python/jq いずれもエラーメッセージ全体を引用するのが安全。

`content/blog/{ja,en,es,pt}/json-comments.md` のパーサー別エラー表は、この
`results.json` の値と完全一致させること (数値・文言の捏造禁止ルール準拠)。

results.json

{
  "generatedAt": "2026-08-27T01:32:14.151Z",
  "environment": {
    "node": "v26.7.0",
    "python": "3.14.6",
    "jq": "1.7.1-apple",
    "jqAvailable": true,
    "json5": "2.2.3",
    "jsoncParser": "3.3.1",
    "os": "Darwin 25.5.0",
    "machine": "Apple M5 Pro"
  },
  "cases": [
    {
      "id": "line-comment-object",
      "description": "Line comment before the first property of a multi-line object (the snippet the article shows)",
      "input": "{\n  // server settings\n  \"host\": \"localhost\",\n  \"port\": 8080 /* default */\n}",
      "parsers": {
        "V8 (Node.js v26.7.0) JSON.parse": {
          "accepted": false,
          "message": "Expected property name or '}' in JSON at position 4 (line 2 column 3)",
          "position": {
            "offset": 4,
            "line": 2,
            "column": 3
          }
        },
        "Python 3.14.6 json.loads": {
          "accepted": false,
          "message": "Expecting property name enclosed in double quotes: line 2 column 3 (char 4)",
          "position": {
            "line": 2,
            "column": 3,
            "offset": 4
          }
        },
        "jq jq-1.7.1-apple": {
          "accepted": false,
          "message": "jq: parse error: Invalid numeric literal at line 2, column 5",
          "position": {
            "line": 2,
            "column": 5,
            "offset": null
          }
        },
        "json5 2.2.3": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "jsonc-parser 3.3.1": {
          "accepted": true,
          "message": null,
          "position": null
        }
      }
    },
    {
      "id": "line-comment-only",
      "description": "Line comment before the first property, no block comment",
      "input": "{\n  // server settings\n  \"host\": \"localhost\"\n}",
      "parsers": {
        "V8 (Node.js v26.7.0) JSON.parse": {
          "accepted": false,
          "message": "Expected property name or '}' in JSON at position 4 (line 2 column 3)",
          "position": {
            "offset": 4,
            "line": 2,
            "column": 3
          }
        },
        "Python 3.14.6 json.loads": {
          "accepted": false,
          "message": "Expecting property name enclosed in double quotes: line 2 column 3 (char 4)",
          "position": {
            "line": 2,
            "column": 3,
            "offset": 4
          }
        },
        "jq jq-1.7.1-apple": {
          "accepted": false,
          "message": "jq: parse error: Invalid numeric literal at line 2, column 5",
          "position": {
            "line": 2,
            "column": 5,
            "offset": null
          }
        },
        "json5 2.2.3": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "jsonc-parser 3.3.1": {
          "accepted": true,
          "message": null,
          "position": null
        }
      }
    },
    {
      "id": "block-comment-after-value",
      "description": "Block comment after a value on a single line",
      "input": "{\"port\": 8080 /* default */}",
      "parsers": {
        "V8 (Node.js v26.7.0) JSON.parse": {
          "accepted": false,
          "message": "Expected ',' or '}' after property value in JSON at position 14 (line 1 column 15)",
          "position": {
            "offset": 14,
            "line": 1,
            "column": 15
          }
        },
        "Python 3.14.6 json.loads": {
          "accepted": false,
          "message": "Expecting ',' delimiter: line 1 column 15 (char 14)",
          "position": {
            "line": 1,
            "column": 15,
            "offset": 14
          }
        },
        "jq jq-1.7.1-apple": {
          "accepted": false,
          "message": "jq: parse error: Invalid numeric literal at line 1, column 17",
          "position": {
            "line": 1,
            "column": 17,
            "offset": null
          }
        },
        "json5 2.2.3": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "jsonc-parser 3.3.1": {
          "accepted": true,
          "message": null,
          "position": null
        }
      }
    },
    {
      "id": "block-comment-before-value",
      "description": "Block comment standing where a value is expected",
      "input": "{\"port\": /* default */ 8080}",
      "parsers": {
        "V8 (Node.js v26.7.0) JSON.parse": {
          "accepted": false,
          "message": "Unexpected token '/', \"{\"port\": /* default\"... is not valid JSON",
          "position": null
        },
        "Python 3.14.6 json.loads": {
          "accepted": false,
          "message": "Expecting value: line 1 column 10 (char 9)",
          "position": {
            "line": 1,
            "column": 10,
            "offset": 9
          }
        },
        "jq jq-1.7.1-apple": {
          "accepted": false,
          "message": "jq: parse error: Invalid numeric literal at line 1, column 12",
          "position": {
            "line": 1,
            "column": 12,
            "offset": null
          }
        },
        "json5 2.2.3": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "jsonc-parser 3.3.1": {
          "accepted": true,
          "message": null,
          "position": null
        }
      }
    },
    {
      "id": "valid-baseline",
      "description": "Same object with no comments - negative control that every parser must accept",
      "input": "{\n  \"host\": \"localhost\",\n  \"port\": 8080\n}",
      "parsers": {
        "V8 (Node.js v26.7.0) JSON.parse": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "Python 3.14.6 json.loads": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "jq jq-1.7.1-apple": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "json5 2.2.3": {
          "accepted": true,
          "message": null,
          "position": null
        },
        "jsonc-parser 3.3.1": {
          "accepted": true,
          "message": null,
          "position": null
        }
      }
    }
  ]
}