CSVをカンマで割るだけでは足りない

表データをJSONへ移すとき、各行を改行で分け、各セルをカンマで分けるだけでは、セルの中にあるカンマや改行を区切りと取り違えます。たとえば "music, video" は一つの値ですが、単純な split(',') では二つの値になります。引用符で囲まれた複数行の文章も、行の区切りで分割すると崩れます。

ToolShedのCSV→JSONは、先頭行を列名として読み、後続行をオブジェクト配列へ変換します。仕事の表を移すときは、変換後のJSONだけでなく、列名・列数・値の型も一緒に確認する必要があります。

この記事の例は架空データです。歌や動画に関する実在の記録ではなく、区切り文字の扱いを示すための入力です。

パーサーの考え方を比べる

表は横にスクロールできます。

同じ入力を処理単純な改行・カンマ分割引用符を読む処理
CSVの物理行4行として読む(ヘッダー+データ断片3行)ヘッダー+データ2件として読む
Sheer Clipのnote"musicvideo" に分裂music, video の1値
ToolShedのnote"line one と次の行に分裂改行を含む1値。二重引用符も復元

引用符を扱う処理では、文字を1つずつ見て「引用符の内側か」を記録します。内側のカンマと改行は値として残し、連続した二重引用符は値中の引用符1つとして読みます。外側にあるカンマと改行だけをセル・行の区切りにします。引用符内の文字を残す必要があるため、単純分割よりこの方式が要件に合います。今回のサンプルでは両方を同じ入力で比べ、2件のデータと値が保たれることをテストします。

次の入力は、引用符内の改行を含むため物理的には4行、ヘッダーを除く論理データは2件です。単純分割では1件目のnoteがカンマで2セルに分かれ、2件目は改行で別の断片になります。

name,count,note
Sheer Clip,2,"music, video"
ToolShed,3,"line one
line ""two"""

列名は最初の行から取得し、各データ行では列名の数だけ値を読みます。CSVはテキストなので、数値に見える 2 もJSONでは文字列の "2" になります。計算に使う場合は、受け取った後に列ごとの型変換を検討してください。

独立テストで再現する

ToolShedの実装からコピーしたTypeScriptファイルとNode.js標準テストを使います。例の1件目にはカンマを含む値、2件目には改行と二重引用符を含む値を入れました。

  1. サンプルとテストを保存する

    CSV変換のサンプルテストファイルを同じフォルダへ保存します。

  2. Node.js 24でテストを実行する

    そのフォルダで node --experimental-strip-types --test --test-isolation=none toolshed-csv-json.test.mjs を実行します。TypeScriptの型注釈を除いてNode.jsから直接読み込む指定です。

  3. 比較と変換を確認する

    5つのテストが引用符内のカンマ・改行・引用符、値の文字列化、往復、列の不足と余剰、同じ入力に対する単純分割との違いを検査します。

2件全体の出力は次のようになります。値は保たれますが、型情報はCSVに含まれないため、数値も文字列として表現されます。

[
  {
    "name": "Sheer Clip",
    "count": "2",
    "note": "music, video"
  },
  {
    "name": "ToolShed",
    "count": "3",
    "note": "line one\nline \"two\""
  }
]

このサンプルはアプリ本体から抽出した関数のコピーで、UIや本番サイトのデータ処理を呼び出すものではありません。手元で仕組みを試すための独立した例です。

テストで確かめた結果

Node.js v24.19.0で独立テスト5件が成功しました。引用符内のカンマは一つのセルに残り、引用符内の改行と二重引用符も復元されます。JSON→CSV→JSONでは、文字列として表せる値とヘッダーの並びを保てました。同じ入力を単純分割した比較では、ヘッダーを除くデータ断片が2件ではなく3件になり、最初のnoteも "musicvideo" に分かれました。

列数の違いも実際の関数に合わせて検査しています。列名より値が少ない行では不足セルが空文字になります。一方、値が多い行では余剰セルが出力JSONへ入りません。列数の不一致をエラーとして止める挙動ではないため、変換前に各データ行のセル数を列名の数と照合してください。

この結果を自分のCSVへ当てはめるなら、まず先頭行の列名、その後に数行の入力・出力を比較し、日付・数値・空欄が意図した形かを確かめます。元データを上書きする前に、変換後の値を使う側でも確認するのが安全です。

使う前に確認したいこと

  • 先頭行が列名として扱われるため、ヘッダーのないCSVはそのままでは同じ意味に変換できません。
  • すべてのCSV値は文字列です。数値演算や日付比較が必要なら、用途に応じて型を決め直します。
  • 同じ列名が重複する場合は、後の値が同じオブジェクトのキーを上書きします。
  • 余剰セルは現在の処理で捨てられます。行ごとの列数を事前に点検してください。
  • このテストは区切りや引用符の代表例を扱いますが、あらゆる入力元のCSV方言を保証するものではありません。

ToolShedで使う場合は、対象ツールのCSV入力へ貼り付け、JSON出力を見てからコピーします。変換自体の説明はToolShedの使い方にまとめています。

参照した実装

コピー元はToolShedのソースリポジトリ webtool のHEAD 88a9ca686 にある packages/tools/src/text/csv-json.ts です。ブログのサンプルはこのファイルをそのまま配置し、変換関数の挙動をNode.jsテストで確かめる構成にしました。

テストは入力例を指定して実行できる確認範囲です。ToolShedの全機能や、本番環境でのファイル保存・通信までをテストするものではありません。