- 設計
CSV・JSON・XML・YAML・TOMLの違いと使い分け|同じデータで比較
データ形式は用途で選びます。表形式の受け渡しはCSV、API・プログラム間はJSON、人が編集する設定はYAMLかTOML、スキーマ検証や文書構造が要るならXMLです。
この記事では、同じデータ(備品予約システムの備品3件)を5つの形式とJSON Linesで書き、Pythonで読み込んだ結果を並べて、型・階層・コメント・サイズの違いを見える形にします。各形式の細かい文法ではなく、「どれを選ぶか」の判断に絞っています。
確認環境:Ubuntu 24.04、Python 3.11.15(標準ライブラリの csv/json/xml.etree.ElementTree/tomllib)、PyYAML 6.0.3、pandas 3.0.2、lxml 6.1.3、jsonschema 4.26.0、tomli-w 1.2.0(確認日 2026-10-06)。
この記事の結論
- 表形式(行と列)の受け渡しはCSV。 表計算ソフトでそのまま開け、サイズも最小。ただし型と階層は持てない。
- API・プログラム間のやり取りはJSON。 型と階層を持ち、ほぼすべての言語で標準的に読める。コメントは書けない。
- 大量のレコードを1件ずつ追記・処理するならJSON Lines。 1行が1件のJSON。
- 人が編集する設定ファイルはYAMLかTOML。 どちらもコメントが書ける。階層が深いならYAML、浅くて型をはっきり書きたいならTOML。
- スキーマで構造を検証したい、文章の中に印を付けたいならXML。
用途別のデータ形式 早見表
| 用途 | 選ぶ形式 | 理由 |
|---|---|---|
| 表計算ソフトで開く一覧、DBのエクスポート・インポート | CSV | 行と列だけの形。サイズが小さい |
| Web APIのリクエスト・レスポンス | JSON | 型と階層を持ち、読み書きの手段が広くそろっている |
| ログ、大量データの受け渡し、1件ずつの追記 | JSON Lines | 1行=1件。途中まででも読め、追記は行を足すだけ |
| 人が手で編集する設定(階層が深い) | YAML | 記号が少なく、入れ子を字下げで書ける。コメント可 |
| 人が手で編集する設定(階層が浅い) | TOML | キー=値の形で、文字列・数値・日付の区別が文法で決まる。コメント可 |
| 構造をスキーマで厳密に検証する文書、文章に印を付ける文書 | XML | 要素・属性・混在内容を表せ、XSDで検証できる |
| プログラムが書き出して別のプログラムが読む設定 | JSON | 人が編集しないならコメントは不要で、型が保たれる |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSV・JSON・XML・YAML・TOMLの違いと使い分け|同じデータで比較」
同じデータを各形式で書く
題材は備品3件です。コード(先頭ゼロあり)、名前(カンマを含むものあり)、在庫数(数値)、有効フラグ(真偽)、タグ(0〜2個の一覧)という、形式の違いが出やすい項目を入れています。
CSV(items.csv)
code,name,stock,active,tags
00123,プロジェクター,2,true,映像;会議室
00456,"延長コード, 5m",8,true,電源
00789,会議用スピーカー,0,false,
カンマを含む値は二重引用符で囲みます。タグのような「1つの項目に複数の値」は表せないため、セミコロンで区切るという独自の約束を足しています。
JSON(items.json)
{
"items": [
{"code": "00123", "name": "プロジェクター", "stock": 2, "active": true, "tags": ["映像", "会議室"]},
{"code": "00456", "name": "延長コード, 5m", "stock": 8, "active": true, "tags": ["電源"]},
{"code": "00789", "name": "会議用スピーカー", "stock": 0, "active": false, "tags": []}
]
}
JSON Lines(items.jsonl)
{"code": "00123", "name": "プロジェクター", "stock": 2, "active": true, "tags": ["映像", "会議室"]}
{"code": "00456", "name": "延長コード, 5m", "stock": 8, "active": true, "tags": ["電源"]}
{"code": "00789", "name": "会議用スピーカー", "stock": 0, "active": false, "tags": []}
全体を囲む角かっこも行末のカンマもなく、1行が1つのJSONの値です。
XML(items.xml)
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item code="00123" active="true">
<name>プロジェクター</name>
<stock>2</stock>
<tags><tag>映像</tag><tag>会議室</tag></tags>
</item>
<item code="00456" active="true">
<name>延長コード, 5m</name>
<stock>8</stock>
<tags><tag>電源</tag></tags>
</item>
<item code="00789" active="false">
<name>会議用スピーカー</name>
<stock>0</stock>
<tags/>
</item>
</items>
YAML(items.yaml)
# 備品一覧
items:
- code: "00123"
name: プロジェクター
stock: 2
active: true
tags: [映像, 会議室]
- code: "00456"
name: 延長コード, 5m
stock: 8
active: true
tags: [電源]
- code: "00789"
name: 会議用スピーカー
stock: 0
active: false
tags: []
TOML(items.toml)
# 備品一覧
[[items]]
code = "00123"
name = "プロジェクター"
stock = 2
active = true
tags = ["映像", "会議室"]
[[items]]
code = "00456"
name = "延長コード, 5m"
stock = 8
active = true
tags = ["電源"]
[[items]]
code = "00789"
name = "会議用スピーカー"
stock = 0
active = false
tags = []
Pythonで読み込んだ結果を並べる
6つのファイルを同じフォルダに置き、次のスクリプトで読み込みます。PyYAML だけ pip install pyyaml が必要で、ほかは標準ライブラリです。
# read_all.py
import csv
import json
import tomllib
import xml.etree.ElementTree as ET
from pathlib import Path
import yaml # PyYAML
def read_csv():
with open("items.csv", encoding="utf-8", newline="") as f:
return list(csv.DictReader(f))
def read_json():
return json.loads(Path("items.json").read_text(encoding="utf-8"))["items"]
def read_jsonl():
with open("items.jsonl", encoding="utf-8") as f:
return [json.loads(line) for line in f if line.strip()]
def read_xml():
rows = []
for item in ET.parse("items.xml").getroot().findall("item"):
rows.append({
"code": item.get("code"),
"name": item.findtext("name"),
"stock": item.findtext("stock"),
"active": item.get("active"),
"tags": [t.text for t in item.findall("tags/tag")],
})
return rows
def read_yaml():
return yaml.safe_load(Path("items.yaml").read_text(encoding="utf-8"))["items"]
def read_toml():
with open("items.toml", "rb") as f: # tomllib はバイナリモードで開く
return tomllib.load(f)["items"]
readers = {"CSV": read_csv, "JSON": read_json, "JSON Lines": read_jsonl,
"XML": read_xml, "YAML": read_yaml, "TOML": read_toml}
results = {name: fn() for name, fn in readers.items()}
for name, rows in results.items():
print(f"--- {name}")
print(rows[1])
print("--- 型(2件目の code / stock / active / tags)")
for name, rows in results.items():
r = rows[1]
print(f"{name:10} " + " ".join(type(r[k]).__name__ for k in ("code", "stock", "active", "tags")))
print("--- 同じ内容になったか")
base = results["JSON"]
for name, rows in results.items():
print(f"{name:10} {rows == base}")
print("--- ファイルサイズ(バイト)")
for fn in ["items.csv", "items.jsonl", "items.json", "items.yaml", "items.toml", "items.xml"]:
print(f"{fn:12} {Path(fn).stat().st_size}")
実行結果(Python 3.11.15、PyYAML 6.0.3):
--- CSV
{'code': '00456', 'name': '延長コード, 5m', 'stock': '8', 'active': 'true', 'tags': '電源'}
--- JSON
{'code': '00456', 'name': '延長コード, 5m', 'stock': 8, 'active': True, 'tags': ['電源']}
--- JSON Lines
{'code': '00456', 'name': '延長コード, 5m', 'stock': 8, 'active': True, 'tags': ['電源']}
--- XML
{'code': '00456', 'name': '延長コード, 5m', 'stock': '8', 'active': 'true', 'tags': ['電源']}
--- YAML
{'code': '00456', 'name': '延長コード, 5m', 'stock': 8, 'active': True, 'tags': ['電源']}
--- TOML
{'code': '00456', 'name': '延長コード, 5m', 'stock': 8, 'active': True, 'tags': ['電源']}
--- 型(2件目の code / stock / active / tags)
CSV str str str str
JSON str int bool list
JSON Lines str int bool list
XML str str str list
YAML str int bool list
TOML str int bool list
--- 同じ内容になったか
CSV False
JSON True
JSON Lines True
XML False
YAML True
TOML True
--- ファイルサイズ(バイト)
items.csv 162
items.jsonl 304
items.json 339
items.yaml 326
items.toml 318
items.xml 478
読み取れることは3つです。
- JSON・JSON Lines・YAML・TOML は、読み込んだ結果がまったく同じ(数値は int、真偽は bool、タグは list)。
- CSVはすべて文字列。 在庫数の 8 も 'true' も文字列で、タグの一覧も1つの文字列のままです。数値や真偽への変換は、読む側のプログラムが決めます。
- XMLも値はすべて文字列。 ただしタグの一覧は要素の繰り返しとして取り出せます。数値への変換は読む側か、後述のスキーマで行います。
5形式+JSON Linesの比較表
| 観点 | CSV | JSON | JSON Lines | XML | YAML | TOML |
|---|---|---|---|---|---|---|
| コメント | 決まりなし | 書けない | 書けない | 書ける | 書ける(# 以降) | 書ける(# 以降) |
| 型 | なし(すべて文字列) | 文字列・数値・真偽・null・配列・オブジェクト | JSONと同じ | なし(文字列。スキーマで型を付ける) | 文字列・数値・真偽・null・日付など(書き方から推定) | 文字列・整数・小数・真偽・日付時刻・配列・テーブル(nullなし) |
| 階層 | なし | あり | あり(1行の中で) | あり(属性・混在内容も) | あり(字下げ) | あり(テーブル。深い入れ子は長くなる) |
| スキーマ検証 | 仕様に含まれない | JSON Schema | 1行ずつJSON Schema | XSD など | JSON Schema を流用できる | JSON Schema を流用できる |
| 1,000件のサイズ(CSV=1.00) | 1.00 | 2.40(改行なし) | 2.40 | 3.46(改行なし) | 2.45 | 2.98 |
| 人が手で編集 | 表計算ソフトで可 | 可(記号が多い) | 向かない(1行が長い) | 可(タグが多い) | しやすい | しやすい |
| 追記のしやすさ | 行を足す | 全体を書き直す | 行を足す | 全体を書き直す | 末尾に足せる(一覧のとき) | 末尾に足せる(テーブル配列のとき) |
| 文字コード | 仕様では決まっていない | UTF-8 | UTF-8 | 宣言で指定 | ― | UTF-8 |
| Python標準ライブラリ | csv | json | json | xml.etree | なし(PyYAML など) | tomllib(読み込みのみ) |
表の根拠は次のとおりです。
- CSVの RFC 4180 は Informational(情報提供)の文書で、型もコメントも定めていません。文字コードも固定されておらず、やり取りの相手と別に決める必要があります。
- JSONの RFC 8259 は、値を「オブジェクト、配列、数値、文字列、false、null、true」と定め、空白として認めるのはスペース・タブ・改行・復帰だけです(コメントの文法はありません)。閉じた環境の外でやり取りするJSONはUTF-8と定められています。
- JSON Lines は、UTF-8であること、各行が正しいJSONの値であること、行の区切りが改行であることの3点が要件で、拡張子は .jsonl が推奨されています。
- TOML v1.0.0 は、ファイルがUTF-8であること、真偽値は小文字の true/false だけであること、# 以降がコメントになることを定めています。null に当たる値はありません。
- Pythonの tomllib は 3.11 で追加された読み込み専用のモジュールで、公式ドキュメントに「書き出しには対応しない」と書かれています。
ファイルサイズを1,000件で比べる
3件では差が小さいので、同じ構造の備品1,000件を各形式で書き出して比べました(Pythonで生成。YAMLは PyYAML、TOMLは tomli-w、XMLは xml.etree で出力)。
| 形式 | バイト数 | CSVを1とした比 | gzip圧縮後のバイト数 |
|---|---|---|---|
| CSV | 35,754 | 1.00 | 6,426 |
| JSON Lines | 85,726 | 2.40 | 6,974 |
| JSON(改行・字下げなし) | 85,737 | 2.40 | 6,985 |
| JSON(字下げ2) | 160,745 | 4.50 | 7,953 |
| YAML | 87,757 | 2.45 | 7,562 |
| TOML | 106,725 | 2.98 | 7,347 |
| XML(改行・字下げなし) | 123,741 | 3.46 | 7,191 |
そのままのサイズはCSVが最小で、JSONは約2.4倍、XMLは約3.5倍でした。gzipで圧縮すると差は1.0〜1.24倍に縮まります。 キー名やタグ名の繰り返しは圧縮がよく効くためです。通信で圧縮が使えるなら、サイズだけを理由に形式を決める必要は小さくなります。
つまずきやすい点を再現する
次のスクリプトで、形式ごとのつまずきをまとめて確かめます(pandas は pip install pandas)。
# pitfalls.py
import csv
import io
import json
import tomllib
import pandas as pd
import yaml
print("=== 1. YAML: no / yes / on / off")
doc = """
country: no
answer: yes
power: on
quoted: "no"
version: 1.10
code: 00123
time: 12:30
"""
for k, v in yaml.safe_load(doc).items():
print(f"{k:8} -> {v!r} ({type(v).__name__})")
print("=== 2. CSV: 先頭ゼロ")
text = "code,name\n00123,プロジェクター\n00456,延長コード\n"
print("csv.DictReader :", [r["code"] for r in csv.DictReader(io.StringIO(text))])
print("int() に変換 :", [int(r["code"]) for r in csv.DictReader(io.StringIO(text))])
print("pandas 既定 :", pd.read_csv(io.StringIO(text))["code"].tolist())
print("pandas dtype=str:", pd.read_csv(io.StringIO(text), dtype=str)["code"].tolist())
print("=== 3. JSON: コメント・末尾カンマ・先頭ゼロ・単一引用符")
cases = {
"コメント": '{\n // ポート番号\n "port": 8080\n}',
"末尾カンマ": '{"port": 8080,}',
"先頭ゼロの数値": '{"code": 00123}',
"単一引用符": "{'port': 8080}",
}
for label, src in cases.items():
try:
print(label, "->", json.loads(src))
except json.JSONDecodeError as e:
print(f"{label} -> JSONDecodeError: {e}")
print("=== 4. TOML: 引用符なしの文字列・null・先頭ゼロ・同じキー")
cases = {
"引用符なしの no": "country = no",
"null": "note = null",
"先頭ゼロの数値": "code = 00123",
"同じキーを2回": "port = 1\nport = 2",
"日付": "released = 2026-10-06",
}
for label, src in cases.items():
try:
print(label, "->", tomllib.loads(src))
except tomllib.TOMLDecodeError as e:
print(f"{label} -> TOMLDecodeError: {e}")
print("=== 5. YAML: 同じキーを2回・インデントのずれ")
print("同じキーを2回 ->", yaml.safe_load("port: 1\nport: 2"))
try:
yaml.safe_load("server:\n host: localhost\n port: 8080\n")
except yaml.YAMLError as e:
print("インデントのずれ ->", type(e).__name__, "/", str(e).replace("\n", " / "))
print("JSON 同じキーを2回 ->", json.loads('{"port": 1, "port": 2}'))
実行結果:
=== 1. YAML: no / yes / on / off
country -> False (bool)
answer -> True (bool)
power -> True (bool)
quoted -> 'no' (str)
version -> 1.1 (float)
code -> 83 (int)
time -> 750 (int)
=== 2. CSV: 先頭ゼロ
csv.DictReader : ['00123', '00456']
int() に変換 : [123, 456]
pandas 既定 : [123, 456]
pandas dtype=str: ['00123', '00456']
=== 3. JSON: コメント・末尾カンマ・先頭ゼロ・単一引用符
コメント -> JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4)
末尾カンマ -> JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 15 (char 14)
先頭ゼロの数値 -> JSONDecodeError: Expecting ',' delimiter: line 1 column 11 (char 10)
単一引用符 -> JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
=== 4. TOML: 引用符なしの文字列・null・先頭ゼロ・同じキー
引用符なしの no -> TOMLDecodeError: Invalid value (at line 1, column 11)
null -> TOMLDecodeError: Invalid value (at line 1, column 8)
先頭ゼロの数値 -> TOMLDecodeError: Expected newline or end of document after a statement (at line 1, column 9)
同じキーを2回 -> TOMLDecodeError: Cannot overwrite a value (at end of document)
日付 -> {'released': datetime.date(2026, 10, 6)}
=== 5. YAML: 同じキーを2回・インデントのずれ
同じキーを2回 -> {'port': 2}
インデントのずれ -> ScannerError / mapping values are not allowed here / in "<unicode string>", line 3, column 8: / port: 8080 / ^
JSON 同じキーを2回 -> {'port': 2}
YAMLの no が false になる
PyYAML 6.0.3 では、引用符なしの no は False、yes と on は True になりました。 YAML 1.1 の真偽値の定義には、y、yes、no、on、off とその大文字の形が含まれており、この結果と一致します。YAML 1.2 では真偽値として扱うのは true と false だけに変わり、yes や no は文字列になります。つまり、同じファイルでも読み込むライブラリが従う版によって結果が変わります。
同じ実行で、1.10 は小数の 1.1 に、00123 は8進数として 83 に、12:30 は60進数として 750 になりました。対策は1つで、文字列として扱いたい値は引用符で囲むことです("no"、"1.10"、"00123"、"12:30")。
CSVの先頭ゼロが落ちる
CSV自体には型が無く、ファイルの中の 00123 は文字のまま残っています。標準ライブラリの csv で読むと '00123' のままでした。落ちるのは、読む側が数値に変換したときです。pandas の read_csv は既定で列の型を推定するため 123 になり、dtype=str を指定すると '00123' のまま読めました。CSVを渡すときは、「この列は文字列として扱う」という約束を仕様に書いておきます。
JSONにコメントを書けない
JSONの文法にコメントはないため、// で始まる行は構文エラーになります。末尾のカンマ、先頭ゼロの数値、単一引用符も同じくエラーでした。設定ファイルに説明を添えたいときは、次のどちらかにします。
| やりたいこと | 方法 |
|---|---|
| 設定値に説明を付けたい | YAML か TOML にする |
| JSONのまま説明を持ちたい | 説明用のキー(例:"_comment")を値として入れる。読む側がそのキーを無視する約束にする |
同じキーを2回書いたとき
同じキーを2回書いた場合、JSON(Python の json)と YAML(PyYAML)は後の値で上書きして読み込み、TOML(tomllib)はエラーで止まりました。 RFC 8259 は、オブジェクト内の名前は一意であるべきとしたうえで、重複したときの動きは実装によって異なると述べています。設定の書き間違いに早く気づきたい場合、TOMLの動きが助けになります。
YAMLとTOMLの選び方
同じ設定を2つの形式で書きます。
# config.yaml
app:
name: 備品予約
debug: false
server:
host: 0.0.0.0
port: 8080
timeout_sec: 30
notify:
enabled: true
channels:
- name: メール
to: [admin@example.com]
- name: チャット
to: [general, alerts]
# config.toml
[app]
name = "備品予約"
debug = false
[server]
host = "0.0.0.0"
port = 8080
timeout_sec = 30
[notify]
enabled = true
[[notify.channels]]
name = "メール"
to = ["admin@example.com"]
[[notify.channels]]
name = "チャット"
to = ["general", "alerts"]
# config_compare.py
import tomllib
from pathlib import Path
import yaml
y = yaml.safe_load(Path("config.yaml").read_text(encoding="utf-8"))
with open("config.toml", "rb") as f:
t = tomllib.load(f)
print(y == t)
print(t["server"])
print(t["notify"]["channels"][1])
True
{'host': '0.0.0.0', 'port': 8080, 'timeout_sec': 30}
{'name': 'チャット', 'to': ['general', 'alerts']}
読み込んだ結果は同じです。違いは書き方に出ます。YAMLは入れ子を字下げで表すので、一覧の中の一覧も短く書けます。TOMLは文字列を必ず引用符で囲み、入れ子は [[notify.channels]] のように見出しで表します。
| 条件 | 選ぶもの | 理由 |
|---|---|---|
| 使うツールが形式を決めている | そのツールの形式 | 選ぶ余地がないため、ツールの文法に従う |
| 入れ子が3段以上、一覧の中に一覧がある | YAML | 字下げで短く書ける。TOMLは見出しが長くなる |
| 入れ子が1〜2段で、セクション分けが主 | TOML | [セクション] とキー=値で読みやすい |
| 文字列か数値か真偽かを、書いた時点ではっきりさせたい | TOML | 文字列は必ず引用符つき。引用符なしの no はエラーになる |
| 同じキーの重複を読み込み時に止めたい | TOML | tomllib はエラーで止まった |
| 日付・時刻を型として持ちたい | TOML | 日付が date 型として読み込まれた |
| 同じ設定のかたまりを何か所かで使い回したい | YAML | アンカーとエイリアスの仕組みがある |
| Pythonで追加のライブラリなしに読みたい | TOML | tomllib が標準ライブラリにある(3.11以降、読み込みのみ) |
| 値が「無い」こと(null)を表したい | YAML | TOMLには null が無い(キーごと書かない形にする) |
スキーマ検証が要るときの選択肢
「この項目は必須」「在庫数は0以上の整数」といった構造の約束を、ファイルの外に定義して機械的に検査するのがスキーマ検証です。XMLはXSD、JSONはJSON Schemaで検証できます。 同じ備品データで確かめます。
<?xml version="1.0" encoding="UTF-8"?>
<!-- items.xsd -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="items">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="stock" type="xs:nonNegativeInteger"/>
<xs:element name="tags">
<xs:complexType>
<xs:sequence>
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="code" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{5}"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="active" type="xs:boolean" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
# validate.py(pip install lxml jsonschema)
import json
from pathlib import Path
from jsonschema import Draft202012Validator
from lxml import etree
# --- XML を XSD で検証
schema = etree.XMLSchema(etree.parse("items.xsd"))
good = etree.parse("items.xml")
print("XML 正しいファイル:", schema.validate(good))
bad = etree.fromstring(Path("items.xml").read_bytes().replace(b"<stock>8</stock>", b"<stock>eight</stock>"))
print("XML stock を eight に:", schema.validate(bad))
for err in schema.error_log:
print(" ", f"line {err.line}: {err.message}")
# --- JSON を JSON Schema で検証
item_schema = {
"type": "object",
"required": ["code", "name", "stock", "active", "tags"],
"properties": {
"code": {"type": "string", "pattern": "^[0-9]{5}$"},
"name": {"type": "string"},
"stock": {"type": "integer", "minimum": 0},
"active": {"type": "boolean"},
"tags": {"type": "array", "items": {"type": "string"}},
},
}
validator = Draft202012Validator(
{"type": "object", "properties": {"items": {"type": "array", "items": item_schema}}})
data = json.loads(Path("items.json").read_text(encoding="utf-8"))
print("JSON 正しいファイル:", validator.is_valid(data))
data["items"][1]["stock"] = "eight"
data["items"][2]["code"] = 789
print("JSON 値を2か所変更:", validator.is_valid(data))
for err in validator.iter_errors(data):
print(" ", list(err.absolute_path), err.message)
実行結果(lxml 6.1.3、jsonschema 4.26.0):
XML 正しいファイル: True
XML stock を eight に: False
line 10: Element 'stock': 'eight' is not a valid value of the atomic type 'xs:nonNegativeInteger'.
JSON 正しいファイル: True
JSON 値を2か所変更: False
['items', 1, 'stock'] 'eight' is not of type 'integer'
['items', 2, 'code'] 789 is not of type 'string'
どちらも、型の違いを場所つきで指摘できました。YAMLとTOMLは、読み込んだ結果がJSONと同じ辞書・リストになるので、同じJSON Schemaでそのまま検証できます(この記事の items.yaml と items.toml は、読み込み結果がJSONと一致することを前の節で確かめています)。
| 条件 | 選ぶもの |
|---|---|
| データが項目と値の集まりで、構造を検証したい | JSON+JSON Schema |
| 文章の途中に印を付ける(段落の中の強調、注記など)、属性と本文を分けて持ちたい | XML |
| 相手先や既存の仕組みがXMLとXSDを前提にしている | XML |
| 表形式で、列の約束だけ決めたい | CSV+項目定義書(列名・型・必須を別紙で決める) |
似た形式との違いと選び方
| 比べるもの | 違い | 選び方 |
|---|---|---|
| JSON と JSON Lines | JSONは全体で1つの値。JSON Linesは1行が1つの値 | 全体を一度に読むならJSON、1件ずつ追記・処理するならJSON Lines |
| CSV と JSON Lines | CSVは列が固定で型なし。JSON Linesは行ごとに項目が違ってもよく、型と階層を持つ | 表計算ソフトで開くならCSV、プログラム同士で階層つきのレコードを流すならJSON Lines |
| YAML と JSON | YAMLはコメントが書け、記号が少ない。JSONは文法が小さく、読み込み結果の揺れが少ない | 人が編集するならYAML、プログラムが書き出すならJSON |
| YAML と TOML | YAMLは深い入れ子に強い。TOMLは型がはっきりし、重複キーを止める | 前の節の表を参照 |
| XML と JSON | XMLは属性・混在内容・名前空間を持つ。JSONは項目と値だけで軽い | データの受け渡しはJSON、文書構造やXSD前提のやり取りはXML |
確かめて分かったこと
- JSON・JSON Lines・YAML・TOML は、同じデータを読み込むと完全に同じ結果になった。 CSVとXMLだけが、すべての値を文字列として返した。
- CSVの先頭ゼロは、ファイルの中では残っていた。 標準ライブラリの csv は '00123' のまま返し、pandas の既定の読み込みで 123 になった。
- PyYAML 6.0.3 では no が False になるだけでなく、1.10 が 1.1、00123 が 83、12:30 が 750 になった。
- 同じキーを2回書くと、JSONとYAMLは後の値が残り、TOMLはエラーで止まった。
- 1,000件のサイズはCSVを1としてJSONが2.40、XMLが3.46。gzip圧縮後は最大でも1.24倍の差に縮まった。
- YAMLとTOMLで書いた同じ設定は、読み込むと一致した。 行数はYAMLが15行、TOMLが20行だった。
よくある質問
Q. CSVとJSONの違いは何ですか? A. CSVは行と列だけの表で型を持たず、JSONは型と階層を持ちます。表計算ソフトで開く一覧はCSV、プログラム間で入れ子のあるデータを渡すならJSONが向いています。
Q. YAMLとJSONの違いは何ですか? A. YAMLはコメントが書けて人が編集しやすく、JSONは文法が小さくプログラムでの読み書きに向いています。読み込んだ結果は、同じデータならどちらも同じ辞書とリストになります。
Q. 設定ファイルはYAMLとTOMLのどちらにすればよいですか? A. 使うツールが形式を決めているならそれに従い、自分で選べるなら、入れ子が深い設定はYAML、浅くて型をはっきりさせたい設定はTOMLにします。
Q. JSONにコメントを書けないときはどうすればよいですか? A. 説明が必要な設定ファイルならYAMLかTOMLに変えるのが素直です。JSONのままにする場合は、説明用のキーを値として持たせ、読む側で無視します。
Q. XMLはどんなときに使いますか? A. 文章の途中に印を付ける文書や、XSDでの検証を前提にしたやり取りで使います。項目と値だけのデータの受け渡しなら、JSONのほうが短く書けます。
まとめ
- 表形式はCSV、API・プログラム間はJSON、人が編集する設定はYAMLかTOML、スキーマ検証や文書構造が要るならXML。
- CSVとXMLは値がすべて文字列で、型は読む側(またはスキーマ)が決める。JSON・YAML・TOMLは型を持つ。
- コメントが書けるのはXML・YAML・TOML。 JSONには書けない。
- YAMLは引用符なしの no・1.10・00123・12:30 が別の型に変わることがある。 文字列は引用符で囲む。
- CSVの先頭ゼロは、読む側が数値に変換したときに落ちる。 文字列として読む指定をする。
- YAMLとTOMLは、入れ子の深さと型の厳密さで選ぶ。 重複キーを止めたい、日付を型で持ちたいならTOML。
- サイズはCSVが最小だが、圧縮後の差は小さい。
参考・出典
- RFC 4180 Common Format and MIME Type for Comma-Separated Values (CSV) Files: https://www.rfc-editor.org/rfc/rfc4180.txt
- RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format: https://www.rfc-editor.org/rfc/rfc8259.txt
- JSON Lines: https://jsonlines.org/
- YAML 1.1 の真偽値の型定義: https://yaml.org/type/bool.html
- YAML 1.2 の変更点(1.1 からの変更): https://spec.yaml.io/main/spec/1.2.2/ext/changes/
- TOML v1.0.0: https://toml.io/en/v1.0.0
- Python 3.11 ドキュメント「tomllib」: https://docs.python.org/3.11/library/tomllib.html
- PyYAML Documentation: https://pyyaml.org/wiki/PyYAMLDocumentation
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSV・JSON・XML・YAML・TOMLの違いと使い分け|同じデータで比較」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。