🐭

【React/Vue】Inputで取得したCSVをJSONに変換する方法 | Offers Tech Blog

2022/08/29に公開

概要

こんにちは、Offers を運営している株式会社 overflow の Software Engineer(主戦場はフロントエンド)の Kazuya です。今回は、Input で取得した CSV ファイルを JSON に変換する方法を紹介します。

はじめに

本記事の内容は、2022 年 8 月時点のものになります。使用しているプラグインのバージョンアップ等で正常に動作しなくなる可能性があります。また、今回紹介するものは一例であり最適な実装でない可能性があります。これらを予めご理解の上、参考にしていただけると幸いです。

実現したいこと

今回実現したいことは、HTML の Input にアップロードされた CSV ファイルを JSON に変換するというものです。今回は Nuxt2 系の環境で実装した例を紹介していきますが、Next.js などでも問題なく実装できると思います。

使用するプラグイン

https://www.npmjs.com/package/papaparse

実装方法

DOM から取得した File オブジェクトをパースする

Input にファイルをアップロードすると、File オブジェクト に格納されます。この格納されたオブジェクトを JSON にパースする必要があります。そこで使用するのが、CSV を解析できる papaparse です。こちらの parse メソッドを使うと今回やりたいことは概ね実現できます。

csv.ts
import { parse } from 'papaparse';

const fileParser = (file: File) => {
  parse(file, {
    complete: (results) => {
      console.log(results?.data);
    },
  });
};

上記のメソッドに File オブジェクトを引数として渡すと、コンソールログ上で JSON 化されたデータを確認できると思います。しかしこのままでは、アプリケーション内でデータを使い回すことはできません。 return すればいいと思いますが、ドキュメントを読んでみると

Doesn't return anything. Results are provided asynchronously to a callback function.
https://www.papaparse.com/docs#local-files

と書かれています。まとめると、このメソッドに File オブジェクトを渡した際は、戻り値はなく、結果はコールバック関数に格納されるということです。更には非同期で処理されるのというおまけ付きです。では、どのようにすればコールバック関数内のデータを扱えるでしょうか?状態管理に含めるというのも手ですが、もう少し汎用的に扱える形で実装したかったため、今回は 非同期で処理される というところに注目して実装してみました。

Promise で非同期処理の結果を返す

非同期処理といえば、Promise の出番です。Promiseresolve を使うことで非同期処理の結果を返すことができます。これを前述したコードに組み込むと以下のようになります。

csv.ts
const fileParser = (file: File) => {
  return new Promise((resolve, reject) => {
    parse(file, {
      complete: (results) => {
        resolve(results?.data);
      },
      error: () => {
        reject(new Error('csv parse err'));
      },
    });
  });
};
<template>
  <input type="file" accept="text/csv" @change="onChangeHandler" />
</template>

<script lang="ts">
import Vue from "vue";
import { fileParser } from "@/lib/csv";

export default Vue.extend({
  methods: {
    async onChangeHandler(event: any) {
      const file = event?.target?.files[0] as File;
      console.log(await fileParser(file));
    },
  },
});
</script>

作成したコードを上記のように Vue 内で呼び出して実際にアップロードしてみると、意図したデータが取得できていると思います。少し回りくどいような感じはしますが、言語やフレームワークに依存させない形で実装できました。

日本語を含む CSV をアップロードすると文字化けしてしまう場合

あるあるなのですが、日本語を含むものを読み込むとしばしば文字化けしてしまうことがあります。これは 文字コード が原因であることがほとんどです。この問題は、読み込む際の文字コードを設定してあげることで解決できます。そこで使用するのが encoding-japanese です。こちらのプラグインを使用して文字コードを設定することで文字化けを解決できます。上記で紹介したコードに組み込むと以下のようになります。

csv.ts
const fileParser = (file: File) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const codes = new Uint8Array(e?.target?.result as ArrayBuffer);
      const encoding = detect(codes) as Encoding;
      const unicodeString = convert(codes, {
        to: 'UNICODE',
        from: encoding,
        type: 'string',
      });
      parse(unicodeString, {
        complete: (results) => {
          resolve(results?.data as _Object[]);
        },
        error: () => {
          reject(new Error('csv parse err'));
        },
      });
    };
    reader.readAsArrayBuffer(file);
  });
};

まとめ

今回は Input で取得した CSV ファイルを JSON に変換する方法を紹介させていただきました。この方法以外でスマートに実装する方法をご存じの方がいれば、コメントなどでご教授いただけると幸いです。

本記事を最後まで読んで頂き、ありがとうございました。「こんな記事を書いてほしい!」などありましたらコメントまでお願いします!

関連記事

https://zenn.dev/offers/articles/20220523-component-design-best-practice
https://zenn.dev/offers/articles/20220418-what-is-bff-architecture
https://zenn.dev/offers/articles/20220519-thinking-about-dark-mode

Offers Tech Blog

Discussion