本文へスキップ
jsonbeautifiers
日本語

JSONをCSVに変換

ネストしたオブジェクトや配列も明示的に処理。サイズ上限もアップロードもありません。

JSON
CSV

貼り付けたものがブラウザの外に出ることはありません。 connect-src の許可リストにより、これは約束ではなくブラウザによる保証になっています。 自分で確かめる

CSVは四角く、JSONは木です。だからどのコンバーターも、いくつもの判断を下さなければなりません。多くはそれを黙って行います。このページは、判断を下し、それを明示し、変更できるようにしています。

ファイルサイズの上限も1日の回数制限もありません。それを課すサーバーが存在しないからです。

列は「和集合」であり、最初のオブジェクトではない

もっとも影響の大きい判断です。先頭要素のキーだけを読むコンバーターは、あとのレコードにしか現れないフィールドを黙って捨てます。そしてそれに気づくのは、レポートから列がひとつ消えている下流の工程です。

このツールは全行のすべてのパスを、最初に現れた順に集めます。処理は遅くなりますが、正しくなります。JavaScriptでもっとも使われているCSVライブラリであるPapa Parseは、既定では最初のオブジェクトを採用し、そうでない挙動にはcolumnsオプションを明示する必要があります。自分で作るなら知っておく価値のある点です。

行の中の配列:4つの方針と、1つの既定値

ここが本当に判断の分かれるところで、正解はなく、あるのは「妥当な既定値」だけです。

インデックス列(既定)
tags.0、tags.1のようになります。情報を失わず、往復もできます。長い配列がひとつあるだけで列数が爆発するのが代償です。
1つのセルに結合
値をセミコロンでつなぎます。読みやすく、情報は失われ、値に区切り文字が含まれていると危険です。
1つのセルにJSONのまま
配列をJSONテキストとしてシリアライズします。情報を失わず簡潔ですが、下流で再パースが必要です。
行に展開
配列の要素ごとに1行を出力し、スカラーの列は繰り返します。pandasのjson_normalizeにおけるrecord_pathです。1対多の関係には正しく、それ以外にはすべて誤りなので、明示的に有効にする方式で、指定したパスにのみ適用されます。

Excelについて、選択の余地がない2点

UTF-8のバイトオーダーマーク
ExcelはCSVの中のUTF-8を判別しません。ファイル先頭にBOMがないと、システムのコードページとしてバイト列を読み、アクセント付き文字も絵文字もすべて文字化けします。ここでBOMが既定で有効なのはまさにそのためで、BOMで詰まるパイプライン向けにワンクリックで無効にできます。
数式インジェクション
=、+、-、@ のいずれかで始まるセルは、Excel、Google スプレッドシート、LibreOfficeで数式として実行されます。あなたが生成したCSVの中の =HYPERLINK("http://evil","click") という値は、他人の表計算ソフトで生きたリンクになります。OWASPはこれをCSVインジェクションと呼びます。該当するセルには既定でアポストロフィを前置し、そうしたことをツールが通知します。

nullと空文字列

JSONではこの2つは別の値ですが、Excelはどちらも空白として表示します。そのため多くのコンバーターは両者をひとつに潰してしまい、区別が失われます。ここではnullをクォートなしの空セル、空文字列をクォート付きの空セルにするので、往復しても情報が残ります。空文字列1つあたり2文字の増加で済み、その価値があります。

How to do this in code

コードでの変換と、正しさを左右する引数について。

py Python、pandas

encoding="utf-8-sig"が、Excelの必要とするBOMをpandasで書き出す方法です。素のutf-8では、Excelが読み違えるファイルになります。

import pandas as pd

# Flatten nested objects to dotted columns
df = pd.json_normalize(records)
df.to_csv('out.csv', index=False, encoding='utf-8-sig')

# One row per element of a nested array
df = pd.json_normalize(records, record_path='items', meta=['id'])
sh jq

文字列の埋め込みではなく@csvを使ってください。クォートの規則を代わりに処理してくれます。

# Union of keys as the header, then the rows
jq -r '(map(keys) | add | unique) as $c
       | $c, (.[] | [.[$c[]]])
       | @csv' records.json > out.csv

# @csv quotes and escapes correctly; @text does not
js JavaScript
import Papa from 'papaparse';

// Pass the union explicitly. Without it, Papa takes the keys of
// the first object and silently drops the rest.
const columns = [...new Set(records.flatMap(Object.keys))];
const csv = Papa.unparse(records, { columns });
go Go
w := csv.NewWriter(f)
w.Write(columns)
for _, rec := range records {
    row := make([]string, len(columns))
    for i, c := range columns {
        row[i] = fmt.Sprint(rec[c])
    }
    w.Write(row)
}
w.Flush()

よくある質問

ExcelでCSVの文字が化けるのはなぜですか?
ファイルはUTF-8ですが、Excelがシステムのコードページとして読んだからです。バイトオーダーマークのオプションは有効のままにしてください。表計算ソフト以外に渡すファイルなら無効にしてください。BOMを最初の列名の一部として扱うパーサーがあるためです。
いくつかのセルがアポストロフィで始まっているのはなぜですか?
=、+、-、@ のいずれかで始まっていたからです。表計算ソフトはそれらを数式として実行します。アポストロフィがそれを無効化します。生の値が必要で、ファイルの行き先を信頼できるなら、このオプションをオフにしてください。
データがオブジェクトの配列でない場合は?
単一のオブジェクトは1行になります。スカラーの配列は1列になります。{"data": [...]}のようなラッパーは内側の配列を使い、それが規則ではなく推測であるため、そう判断したことを明示します。
ヨーロッパ版Excelにはどの区切り文字を使えばよいですか?
セミコロンです。Excelはシステムのリスト区切り文字から区切り文字を決めており、小数点にカンマを使うロケールではそれがセミコロンになります。ドイツやフランスの環境でカンマ区切りのファイルが1列として開かれるのは、そのためです。