TOON(トゥーン)は「Token-Oriented Object Notation」の略で、JSONと同じデータをLLMのプロンプトに渡すとき、より少ないトークンで書けるように設計されたテキスト形式です。キーを一度だけ宣言して値を行で並べる表形式が中心で、同じ形のオブジェクトが並ぶ配列ほど削減幅が大きくなります。
ただし、どんなデータでも減るわけではありません。入れ子が深いデータや形がそろっていない配列では、改行と空白を詰めたJSON(compact JSON)のほうがトークンが少なくなる場合があります。本記事では2026年7月に出た仕様v4.1の書き方と、3種類のデータで測ったトークン数、言語ごとの実装の対応状況をまとめます。
まとめ:TOONの意味とJSONから切り替える条件
- 意味:JSONのデータモデル(オブジェクト・配列・文字列・数値・真偽値・null)を、インデントと表形式で書き直すLLM入力用の形式。仕様はv4.1(2026-07-26・Working Draft)、MITライセンス
- 効く場面:同じフィールドを持つレコードの配列。実測では100行の社員データで整形JSON比-66.8%、compact JSON比-40.7%
- 効かない場面:深い入れ子、形がばらばらの配列。compact JSON比で+2.9%〜+25.6%と逆に増えた。純粋な表ならCSVのほうが小さい
- 精度:公式ベンチマークの正答率はTOON 72.2%・JSON 71.4%。差の0.8ポイントは信頼区間の幅(±2.8)より小さく、「TOONで精度が上がる」根拠にはならない
- 注意点:v4.0(2026-07-22)で構文が変わり、PythonやRustなどの実装はv4の出力を読めないものがある。言語をまたいで受け渡すなら仕様の版をそろえる
TOONの意味と仕様の現在地
TOONはJohann Schopplich氏が作成し、GitHubのtoon-format組織で仕様と実装が管理されています。仕様書(SPEC.md)の要旨は、TOONを「JSONデータモデルを、明示的な構造と最小限の引用符で表す、行指向・インデントベースのテキスト形式」と定義しています。JSONを置き換える汎用フォーマットではなく、JSONで持っているデータをLLMに渡す直前に変換する用途を想定しています。
| 項目 | 内容(2026年9月時点) |
|---|---|
| 正式名称 | Token-Oriented Object Notation |
| 仕様の版 | v4.1(2026-07-26)・Working Draft |
| ライセンス | MIT |
| 拡張子 | .toon |
| メディアタイプ | text/toon(暫定)・常にUTF-8 |
| 公式実装 | @toon-format/toon 4.1.1(TypeScript) |
| 公式CLI | @toon-format/cli 4.1.1 |
仕様は短期間に大きく動いています。2026年5月にv3.2・v3.3、7月22日にv4.0、7月26日にv4.1が出ており、2025年に書かれた解説の構文やオプションは今の実装と合わない部分があります。代表例が後述するkeyFoldingの削除です。
JSON・YAML・CSVとの違い(同じデータで比較)
3件の注文データをJSONとTOONで書くと次のようになります。TOONではorders[3]が「要素数3」を、{id,customer{name,pref},total,status}がフィールドの並びを宣言し、各行は値だけを並べます。
{
"orders": [
{ "id": 101, "customer": { "name": "佐藤", "pref": "東京" }, "total": 4800, "status": "shipped" },
{ "id": 102, "customer": { "name": "鈴木", "pref": "大阪" }, "total": 12500, "status": "pending" },
{ "id": 103, "customer": { "name": "高橋", "pref": "福岡" }, "total": 3200, "status": "shipped" }
]
}
orders[3]{id,customer{name,pref},total,status}:
101,佐藤,東京,4800,shipped
102,鈴木,大阪,12500,pending
103,高橋,福岡,3200,shipped
o200k_baseで数えると、掲載したJSONは133トークン、同じデータをJSON.stringify(data, null, 2)で整形したJSONは157、compact JSONは84、TOONは60でした。TOONをデコードすると元のJSONと完全に一致します。4形式の違いは次のとおりです。
| 観点 | JSON | YAML | CSV | TOON |
|---|---|---|---|---|
| 入れ子 | 波かっこ | インデント | 表せない | インデント |
| キーの記述 | 要素ごと | 要素ごと | ヘッダー1回 | 表形式ではヘッダー1回 |
| 要素数の宣言 | なし | なし | なし | 非空配列は[N]で宣言 |
| 文字列の引用符 | 常に必要 | 条件付き | 条件付き | 条件付き |
| 主な用途 | API・保存 | 設定ファイル | 表計算・集計 | LLMへの入力 |
TOONはYAMLのインデントとCSVの表を組み合わせた形式と言えます。CSVとの違いは、入れ子を表せることと、[N]で行数を宣言するため途中で切れたデータを検出できることです。
TOONの書き方:仕様v4.1の基本構文
オブジェクトとインデント
オブジェクトはキー: 値で書き、入れ子は波かっこの代わりにインデント(既定は半角スペース2つ)で表します。タブによるインデントは使えません。
server:
host: api.example.com
port: 8080
tls:
enabled: true
配列の3つの書き方
配列は中身の形によって書き方が決まります。v4.1では、表形式の条件を満たし、フィールド付きヘッダーを置ける位置にある配列は、必ず表形式で出力されます。
- インライン形式:文字列・数値・真偽値・nullからなる配列。
tags[3]: api,web,batch - 表形式:全要素でキーと値の形がそろい、末端の値がプリミティブとなる非空オブジェクトの配列。
users[2]{id,name}:の下に1行1件 - リスト形式:形がそろわない配列。各要素を
-で始める
items[3]:
- id: 1
name: A
- id: 2
name: B
tags[2]: x,y
- id: 3
空配列はtags: []と書きます。v4.1より前のtags[0]:という書き方はエンコーダーが出力しなくなりましたが、デコーダーは引き続き受け付けます。
v4.0で追加された入れ子フィールドとキー付き表形式
v4.0では表形式が2方向に広がりました。1つは、先ほどのcustomer{name,pref}のように、入れ子のオブジェクトを列グループとして表の中に畳み込む書き方です。もう1つは、値の形がそろったオブジェクトをキー名[件数:]{フィールド}:で表にする「キー付き表形式」で、機能フラグのようにキー名で引く設定データが1行1件になります。
flags[3:]{enabled,rollout}:
darkMode: true,50
newCheckout: false,0
betaSearch: true,10
同じv4.0で、#で始まる行はコメントとして扱われるようになりました。プロンプトに渡すデータへ注記を書き込めますが、v3で#から始まる値を引用符なしで保存していた場合、v4のデコーダーはその行をコメントとして捨てます。v3時代のTOONを保存しているなら、正規表現/^ *#/で該当行を探すよう仕様のCHANGELOGが案内しています。
一方でkeyFolding(a.b.c: 1のようにキーを連結して入れ子を畳む機能)と、対になるexpandPathsはv4.0で仕様から削除されました。4.1.1の型定義にもこれらのオプションはありません。2025年の記事にあるkeyFolding: 'safe'の例はそのままでは動きません。
引用符が必要な文字列と区切り文字
文字列は原則として引用符なしで書けます。日本語や絵文字、途中の空白もそのままで構いません。仕様7.2節が引用符を必須とするのは、次のような値です。
- 空文字列、前後に空白がある文字列
true・false・nullと同じ文字列、"007"や"+1"のように数値に見える文字列- コロン、二重引用符、バックスラッシュ、角かっこ、波かっこを含む文字列
- 使用中の区切り文字(既定はカンマ)を含む文字列
-または#で始まる文字列
区切り文字はカンマ・タブ・パイプ(|)から選べます。値にカンマを多く含むデータ(住所や文章)はタブにすると引用符が減ります。ただし、100行の社員データではタブにしてもトークン数は1,590のまま変わらず、効果はデータ次第です。
トークン削減効果の実測:データ形状による増減
形の違う3つのデータを@toon-format/toon 4.1.1で変換し、gpt-tokenizer 4.0.0(o200k_base)でトークン数を数えました(2026-09-26実行)。公式ベンチマークと同じトークナイザーを使います。次のスクリプトで再現できるのは、表の社員100行の測定結果です。
import { encode } from '@toon-format/toon'
import { encode as tok } from 'gpt-tokenizer/encoding/o200k_base'
const depts = ['営業', '開発', '人事', '経理']
const prefs = ['東京', '大阪', '福岡', '札幌', '名古屋']
const emp = {
employees: Array.from({ length: 100 }, (_, i) => ({
id: i + 1, name: `社員${i + 1}`, dept: depts[i % 4], pref: prefs[i % 5],
salary: 400 + (i * 7) % 300, active: i % 3 !== 0,
})),
}
const count = (d) => ({
pretty: tok(JSON.stringify(d, null, 2)).length,
compact: tok(JSON.stringify(d)).length,
toon: tok(encode(d)).length,
})
console.log(count(emp))
コードをmeasure.mjsとして保存し、npm install @toon-format/[email protected] [email protected]で依存パッケージを入れたディレクトリでnode measure.mjsを実行します。
| データ | 整形JSON | compact JSON | TOON | compact比 |
|---|---|---|---|---|
| 社員100行(6項目・全行同形) | 4,784 | 2,680 | 1,590 | -40.7% |
| サーバー設定(深い入れ子) | 201 | 102 | 105 | +2.9% |
| イベントログ30件(2種混在) | 1,374 | 755 | 948 | +25.6% |
社員データはCSVにすると1,385トークンで、TOONより約13%少なくなりました。TOONが「JSONより30〜60%少ない」と言われるのは整形JSON(インデント2)と比べた数字で、改行を詰めたJSONと比べると、形のそろわないデータでは逆転します。
公式READMEのベンチマーク(2026年9月時点)も同じ傾向です。入れ子や半構造のデータ5種の合計では、TOONは整形JSONより32.7%少ない一方、compact JSONより1.6%多くなっています。全行同形のデータ3種の合計では整形JSON比-58.7%、CSV比+5.9%です。
トークン数はモデルのトークナイザーで変わります。o200k_baseはOpenAI系の方式で、ClaudeやGeminiでは絶対値が異なるため、採用前に使うモデルのトークンカウントAPIで測り直してください。日本語の文字列がどう分割されるかはトークナイザーの方式と日本語での違いで詳しく扱っています。
LLMの回答精度:公式ベンチマークの読み方と出力側の結果
公式ベンチマークは、4モデル(claude-haiku-4-5・gemini-3.6-flash・gpt-5.4-nano・grok-4.5)に244問のデータ検索問題を解かせ、形式ごとの正答率を比べています(計5,856回の呼び出し)。
| 形式 | 正答率 | 平均トークン |
|---|---|---|
| TOON | 72.2% ±2.8 | 2,474 |
| JSON(整形) | 71.4% ±2.8 | 4,308 |
| XML | 70.7% ±2.9 | 4,909 |
| YAML | 70.1% ±2.9 | 3,487 |
| JSON(compact) | 69.0% ±2.9 | 2,892 |
±はWilsonの95%信頼区間です。READMEは区間が重なる差を「統計的に意味がない」と扱っていますが、区間の重なりは有意差の有無を厳密に判定するものではなく目安です。この評価で観測された正答率の差は0.8ポイントで、「TOONにすると精度が上がる」ではなく「この評価では平均トークン数が約4割少なく、正答率の観測値は近かった」と読むのが適切です。モデル別ではgpt-5.4-nanoでTOON 57.0%がJSON 57.4%とXML 59.4%を下回っており、モデルによって順位が入れ替わります。
構造の検証問題では、今回の評価で正答率に大きな差が出ました。ただし、対象は5問を4モデルに出した計20件です。行が欠けたデータや行数・列数の合わないデータを見抜く問題で、TOONは100%、整形JSONは50%でした。[N]と{fields}の宣言があるため、モデルが宣言と実際の行数の食い違いに気づけます。
出力側は事情が異なります。2026年2月のarXiv論文(Matveev, 2603.03306)はLLMにTOONを生成させる実験を行い、素のJSON生成が最も正確だったと報告しています。TOONの書き方をプロンプトで教える分の「prompt tax」が短い出力では削減分を上回り、単純な構造ではJSON Schemaによる制約付きデコードのほうがトークンも少なかったとしています。モデルに返させる形式は、従来どおりJSON Schemaで出力形式を保証する構造化出力を使い、TOONは入力側に限るのが現実的です。
TypeScript・CLIでJSONをTOONに変換する手順
公式のTypeScript実装はnpmで入ります。encodeでJSON互換の値をTOON文字列に、decodeでTOONを値に戻します。
npm install @toon-format/toon
import { encode, decode } from '@toon-format/toon'
const data = {
users: [
{ id: 1, name: 'Ada', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
],
}
const toon = encode(data)
console.log(toon)
const tsv = encode(data, { delimiter: '\t' })
const back = decode(toon)
console.log(JSON.stringify(back) === JSON.stringify(data))
1つ目のconsole.logはusers[2]{id,name,role}:に続けて1,Ada,adminと2,Bob,userの2行を出力し、2つ目はtrueを出力します。decodeは既定でstrict: trueになっており、宣言した行数と実際の行数が合わないと例外を投げます。
decode('users[3]{id,name}:\n 1,Ada\n 2,Bob')
// Error: Line 3: Expected 3 tabular rows, but got 2
decode('users[3]{id,name}:\n 1,Ada\n 2,Bob', { strict: false })
// { users: [ { id: 1, name: 'Ada' }, { id: 2, name: 'Bob' } ] }
LLMの出力をTOONで受け取る場合は、この厳格モードの例外を「途中で切れた応答」の検出に使えます。コードを書かずに変換とトークン数の比較だけしたいときはCLIが便利です。
npx @toon-format/cli input.json -o output.toon
npx @toon-format/cli data.toon -o output.json
npx @toon-format/cli data.json --stats
--statsを付けると「~20 (JSON) → ~14 (TOON)」のように推定トークン数と削減率が表示されます。プロンプトに埋め込むときは、公式ガイドのとおりコードブロックにして言語名をtoonとし、構文の説明文を書くより表形式の例を1つ見せるほうがモデルは読み取りやすくなります。
言語別実装と仕様バージョンのずれ
TypeScript以外の実装は、各リポジトリの説明に「Community-driven」とある有志の移植で、追従している仕様の版がそろっていません。2026年9月26日時点の各レジストリとREADMEの記載は次のとおりです。
| 言語 | パッケージ | 最新版 | 対象仕様 |
|---|---|---|---|
| TypeScript | @toon-format/toon | 4.1.1 | v4.1 |
| Java | dev.toonformat:jtoon | 2.0.4 | v4.1系(READMEの表記) |
| Rust | toon-format | 0.5.0 | v3.0(READMEの表記) |
| Python | toon-format | 0.9.0b1(ベータ) | v4.0以前に公開 |
Javaはimplementation("dev.toonformat:jtoon:2.0.4")(Gradle)で追加でき、2026年9月にも更新されています。Pythonのtoon-formatはPyPIの最終更新が2025-11-08のベータ版で、READMEは「1.0.0までAPIが変わる可能性がある」としています。
Pythonは入れ方にも落とし穴があります。PyPIの安定版0.1.0は名前を確保するためのパッケージで、encodeを呼ぶとNotImplementedErrorになります。Python 3.14の環境でpip install toon-formatを実行すると、pipはプレリリースを選ばないため、この0.1.0が入りました。実装入りのベータ版を使うには、--preを付けるかtoon-format==0.9.0b1と版を指定します。
版のずれは実害につながります。TypeScript 4.1.1が出力したv4の文書をPython版0.9.0b1のdecodeに渡すと、入れ子フィールドの表はToonDecodeError: Missing colon after keyで止まり、キー付き表形式は例外を出さずに{'flags[3': ']{enabled,rollout}:'}という壊れた辞書を返しました。仕様のCHANGELOGも、v3の非厳格デコーダーはキー付き表形式を「黙って誤って読む」と警告し、エンコーダーより先にデコーダーを更新するよう求めています。
言語をまたぐシステムでは、サービス間の受け渡しはJSONのままにして、LLMを呼び出す直前の1か所だけでTOONに変換するのが安全です。TOONのまま複数の言語で読み書きするなら、送り手と受け手が同じ仕様版を対象にしているかをREADMEで確認してから採用してください。
TOONを採用しない方がよい場面
公式READMEにも「When Not to Use TOON」という節があり、実測の結果と合わせると次の4つの場面ではJSONやCSVのままにしたほうが得です。
- 深い入れ子や形のそろわない配列:表形式にできる割合が0%に近いと、compact JSONのほうが少なくなる。実測では+2.9%と+25.6%
- 純粋な表データ:CSVのほうが小さい。READMEはTOONの5〜10%の上乗せを「行数や列の宣言と引き換えの信頼性のコスト」と説明している
- 応答速度が最優先の環境:ローカルモデルや量子化モデルでは、トークンが多くてもcompact JSONのほうが速く処理される例があるとREADMEが注記している。初回トークンまでの時間と総時間を自分の環境で測る必要がある
- LLMに出力させる形式:前述の論文のとおり、生成は素のJSONのほうが正確だった
入力トークンの費用を下げる手段はTOONだけではありません。同じシステムプロンプトや参照資料を繰り返し送っているなら、プロンプトキャッシュで繰り返し部分の単価を下げるほうが、形式の変換より手間が少なく効果も大きい場合があります。長いデータがコンテキストウィンドウのトークン上限に収まらないことが課題なら、TOONによる圧縮が効きます。
よくある質問
TOONの読み方と意味は何ですか?
読み方は「トゥーン」で、Token-Oriented Object Notationの頭文字です。英語のtoonはcartoon(漫画・アニメ)の略語として使われ、3DCGのトゥーンシェーディングなども同じ語ですが、データ形式のTOONとは関係がありません。
TOONはJSONの代わりになりますか?
APIのやり取りや保存用のJSONを置き換えるものではありません。主にJSONのデータをLLMのプロンプトに入れる直前に変換する用途を想定しています。本記事では、ほかのシステムとの受け渡しにはJSONを推奨します。
TOONでトークンは何%減りますか?
データの形で変わります。本記事の社員100行では整形JSON比で66.8%減りましたが、深い入れ子や形のそろわない配列では、compact JSONより増えることがあります。
TOONはPythonで使えますか?
PyPIのtoon-formatで使えますが、実装が入っているのは2025年11月公開のベータ版(0.9.0b1)で、pip install --pre toon-formatのように明示して入れる必要があります。仕様v4.0以降の入れ子フィールドやキー付き表形式は正しく読めないため、TypeScript版の出力を受け取る用途には向きません。
TOONはJavaで使えますか?
有志のJava実装JToonがMaven Centralで公開されており、座標はdev.toonformat:jtoon、2026年9月時点の最新版は2.0.4です。仕様v4.1系への対応は、使用するリリースの適合テストで確認してください。