Laravel + React で CSV ファイル → JSON 化する方法

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、すでにLaravel + Reactシリーズの記事を公開しはじめてから1か月ほど経ちました。

個人的な感想としては着実に「Vue 3よりこっちのほうが好き」になってきているのを感じていて、やりたいこともいろいろ思い浮かぶ今日この頃です。

そして、そんな中思いついたのが・・・・・・

CSV ファイルを読み込み、中身を JSON 化する

機能です。

というのも、私の場合ごくまれに「CSV をオンラインで JSON 化したい」となるんですが「だったら自分で作ってしまおう!」となったわけですね。

そこで❗

今回は「Laravel + React」で「CSV ファイル → JSON 化」する機能を実装してみます。

ぜひ何かの参考になりましたら嬉しいです。😄

「お酒のんで帰ると、
多めにご飯食べちゃいません??
お腹イタイ…」

開発環境: Laravel 9.x、React、Inertia.js、Vite、TailwindCSS

やりたいこと

今回やりたいことは、次のとおりです。

  • CSV ファイルを選択すると自動で JSON 化する
  • 1行目をキーにした「オブジェクトの配列」に変換できる
  • エンコードは、「utf-8」と「shift_jis」に対応する(※)

※ 気持ちとしては、もうそろそろShitf_JISはなくなってほしいですね…😂

では、実際に作業をしていきましょう❗

パッケージをインストールする

まずは今回の機能に必要なパッケージ papaparse をインストールします。

npm i papaparse --save

ちなみに、本家のサイトには「People 💗 Papa」と書いていて「いやいや!その表現は誤解されるでしょ…😱」となります(わざとでしょうか😂)

ルートをつくる

次にブラウザから実際にアクセスすることになるルートをセットします。

routes/web.php

use Inertia\Inertia;

// 省略

Route::get('csv_parser', fn() => Inertia::render('CsvParser/Index'));

※ 毎回書きますがテストなので1行で書いています。本番は今後の拡張も見据えてコントローラーを使ったほうがいいかもです。

ビューをつくる

では、いきなり今回のメイン部分です。
まずはコードからご覧ください。

resources/js/Pages/CsvParser/Index.jsx

import {useState, useEffect} from 'react';
import Papa from 'papaparse';

export default function Index() {

    // Config
    const [isObject, setIsObject] = useState(false);
    const [encoding, setEncoding] = useState('utf-8');
    const encodings = [
        'utf-8',
        'shift_jis'
    ];

    // Json parser
    const [file, setFile] = useState(null);
    const [json, setJson] = useState('');
    const handleFileChange = e => {

        const files = e.target.files;

        if(files.length > 0) {

            setFile(files[0]);

        }

    };
    useEffect(() => {

        if(file instanceof File) {

            Papa.parse(file, {
                header: isObject,
                encoding: encoding,
                skipEmptyLines: true,
                complete(results) {

                    const csvData = results.data;
                    const jsonText = (csvData.join('') !== '')
                        ? JSON.stringify(csvData)
                        : '';
                    setJson(jsonText);

                },
                error() {

                    alert('CSVファイルの読み込みに失敗しました。');

                }
            });

        }

    }, [file, isObject, encoding]);

    return (
        <div className="p-5">
            <h1 className="mb-5 font-bold">Laravel + React で CSV ファイル → JSON 化する方法</h1>
            <div className="my-5">
                <label className="bg-blue-500 text-white font-bold py-2 px-4 rounded mr-3">
                    CSV ファイルを選択
                    <input type="file" className="hidden" accept="text/csv" onChange={handleFileChange} />
                </label>
                <select onChange={e => setEncoding(e.target.value)}>
                    {encodings.map(encoding => (
                        <option key={encoding} value={encoding}>{encoding}</option>
                    ))}
                </select>
            </div>
            {json && (
                <div>
                    <small className="font-bold">取得されたデータ(JSON)</small>
                    <br />
                    <textarea
                        className="block p-2.5 w-1/2 h-40 text-sm text-gray-900 bg-gray-50 mb-1"
                        value={json}
                        readOnly>
                    </textarea>
                    <div className="px-1">
                        <label>
                            <input type="checkbox" checked={isObject} onChange={e => setIsObject(e.target.checked)} /> <small>1行目をキーにする</small>
                        </label>
                    </div>
                </div>
            ) || (
                <div className="bg-gray-100 p-2">CSV データが見つかりません。</div>
            )}
        </div>
    );

}

この中で一番重要なのが、useEffect()を使っている部分です。

というのも、今回はJSON化する条件が以下2つあるため、ファイルを読み込んだ時点でPapaparseにはかけず、選択されたファイルを保持し続ける必要があるためです。

  • エンコードは「utf-8 or shift_jis」
  • 1行目のキーを使ってオブジェクト化するかどうか

つまり、isObjectencoding、もしくはfileのいずれかに変更があったときにuseEffect()が起動されることになり、ここではじめてPapaparseを使ってCSVJSON化しているという流れになっています。

ちなみに、<textarea> 〜 </textarea>readOnlyがついていますが、もしこれが無いと以下のような警告がコンソールに表示されるためです。

Warning: You provided a `value` prop to a form field without an `onChange` handler.
This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.

(超意訳:口は悪いけど心優しい荒くれ者 編)
onChangeがないのに、valueプロパティ使っちゃダメに決まってんだろ!だって読み込み専用なんだからな。もしそれでもやりてぇんなら、defaultValueを使おうぜ。それか、onChangereadOnlyをつけな!

ということで今回はJSONデータ部分は変更される必要はないので、readOnlyをつけたとうわけです。

なお、useEffect()内で以下のようにfileの中身をチェックしているのは、useEffect()はページ読み込み時にも実行されるためです。つまり、エラー回避のためにつけているわけですね。

if(file instanceof File) {

    // 省略

}

作業は以上です。
お疲れ様でした❗

テストしてみる

では実際にテストをしてみましょう。

今回使うCSVは以下のもので、utf8shift_jis版の2種類を用意しました。

※ 必要でしたら、以下からダウンロードできます。

サンプルCSV(utf-8 & shift_jis)

ではViteを起動してブラウザから「http://******/csv_parser」へアクセスしてください。

すると以下のようなページが表示されます。

では、「CSV ファイルを選択」ボタンをクリックしてutf-8版のCSVを選択してみます。

すると・・・・・・

はい❗
CSVの中身が表示されました。

これは、CSVの中身をそのまま配列化したものですね。

では、次に「1行目をキーにする」にチェックをいれてデータ構成を変更できるか確認してみましょう。

どうなるでしょうか・・・・・・

はい❗
ちょっと文字が小さいのでわかりにくいですが、先ほどは「配列の配列」だったのが、今は「オブジェクトの配列」になりました。

成功です😄

では続いて、この状態でshift_jis版のCSVを読み込んでみます。

どうなったでしょうか・・・・・・

はい❗
表示はできましたが、文字化けしています。

これはもちろんutf-8として読み込まれたからですね。
そこで、エンコードをshift_jisに変更してみます。

うまくいくでしょうか・・・・・・

はい❗
文字コードを変更した途端、文字化けが解消されました。

すべて成功です😄✨

デモページを用意しました

せっかくなので、デモページをつくりました。
ぜひ以下から体験してみてください。

📝 デモページ

企業様へのご提案

今回見ていただいたように、React + Viteを使うと、とてもコードが短くても強力な機能を実装することができます。

こうすることで、開発効率やメンテナンスのしやすさが向上するでしょう。

もしそういったご希望をお持ちでしたら、いつでもお気軽にお問い合わせからご連絡ください。

お待ちしております。😄✨

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで今回は、Laravel + Reactで「CSV → JSON 変換器」を作ってみました。

正直なところ変換部分はPapaparseがすべて引き受けてくれるのでReactの使い方さえわかっていれば逆に「JSON → CSV」へ変換する機能もすぐつくれると思います。(Papaparseにはunperse()というメソッドがあります)

ぜひ皆さんも実際に試してみてくださいね。

ではでは〜❗

「なぜか右足の袖だけが
自転車にこすれるので
汚れるんですが…」

このエントリーをはてなブックマークに追加       follow us in feedly