Node.jsでCSVファイルを読みこんでJSONデータに変換するサンプルコード

Node.jsでShift_JIS で書かれたCSVファイルを取り扱う必要があり、その際の備忘メモ。
**JSON変換までできた方が便利**なので、[csvtojson](https://www.npmjs.com/package/csvtojson) を使ってみました。


## 要約

- csvtojsonを使えば、標準的なCSVファイルを**簡単にJSONデータ**に変更できました。
- Node.jsはファイル読み込みは基本UTF-8を想定しているようですが、UTF-8以外の場合は [iconv-lite](https://www.npmjs.com/package/iconv-lite)をかましてShift_JIS→UTF-8変換してあげればOKでした。
- **csvtojson はコマンドラインからも呼び出せる**ので、コマンドラインでCSV→JSON変換できるのはなにげに便利です。


## 前提や環境

```console
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.3
BuildVersion:   19D76

$ node --version
v10.16.2
$ 
```

ちなみに**サンプルデータは [住所.jp](http://jusyo.jp/csv/new.php) という日本の住所情報**を用いてみました。


## やってみる


```
$ git clone --branch 0.1.0 https://github.com/masatomix/csv-sample-node.git
$ cd csv-sample-node/
$ npm install
```

落としてきたプロジェクトのディレクトリ構成はこんな感じです。処理対象の**csvファイル(``13tokyo.csv``)だけは、[上記サイト](http://jusyo.jp/csv/new.php) から落として**おきましょう。


```console

$ tree
.
├── data
│   └── 13tokyo.csv  ←読み込むCSVファイル
├── src
│   └── index.ts
├── dist
│   ├── index.js
│   └── index.js.map
├── package.json
└── tsconfig.json
```

ソースコードはこんな感じ。

```typescript:index.ts
import fs from 'fs'
import iconv from 'iconv-lite'
import csv from 'csvtojson'

/**
 * 指定したパスのcsvファイルをロードして、JSONオブジェクトとしてparseする。
 * 全行読み込んだら完了する Promise を返す。
 * @param path
 */
const parse = (path: string): Promise<any[]> => {
  return new Promise((resolve, reject) => {
    let datas: any[] = []
    fs.createReadStream(path)
      .pipe(iconv.decodeStream('Shift_JIS'))
      .pipe(iconv.encodeStream('utf-8'))
      .pipe(csv().on('data', data => datas.push(JSON.parse(data)))) // 各行読んだらココが呼ばれるので配列にpush
      .on('end', () => resolve(datas)) // 全部終わったらココにくるので、resolveする
  })
}

if (!module.parent) {
  // 呼んでみる
  parse('./data/13tokyo.csv').then((results: any[]) => {
    // 郵便番号が「100-000x」のものに絞ってみた
    results = results.filter(address => address['郵便番号'].startsWith('100-000'))
    console.table(results)
    // for (const address of results) {
    //   console.log(address)
    // }
  })
}
```

実行してみます。

```console
$ npm run dev

> csv-sample-node@0.1.0-SNAPSHOT dev /Users/xxx/git/csv-sample-node
> ts-node src/index.ts

┌─────────┬─────────────┬────────┬─────────┬─────────────┬────────────┬────────┬───────┬───────┬──────────┬────────┬────────┬────────┬────────────┬──────────┬───────┬───────┬──────────┬────┬──────┬────────┬───────┬───────┐
│ (index) │    住所CD     │ 都道府県CD │ 市区町村CD  │    町域CD     │    郵便番号    │ 事業所フラグ │ 廃止フラグ │ 都道府県  │  都道府県カナ  │  市区町村  │ 市区町村カナ │   町域   │    町域カナ    │   町域補足   │ 京都通り名 │  字丁目  │  字丁目カナ   │ 補足 │ 事業所名 │ 事業所名カナ │ 事業所住所 │ 新住所CD │
├─────────┼─────────────┼────────┼─────────┼─────────────┼────────────┼────────┼───────┼───────┼──────────┼────────┼────────┼────────┼────────────┼──────────┼───────┼───────┼──────────┼────┼──────┼────────┼───────┼───────┤
│    0    │ '100000000' │  '13'  │ '13101' │ '131010000' │ '100-0000' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │   ''   │    '　'     │ '（該当なし）' │  ''   │  ''   │    ''    │ '' │  ''  │   ''   │  ''   │  ''   │
│    1    │ '100000400' │  '13'  │ '13101' │ '131010006' │ '100-0004' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │ '大手町'  │  'オオテマチ'   │    ''    │  ''   │  ''   │    ''    │ '' │  ''  │   ''   │  ''   │  ''   │
│    2    │ '100000200' │  '13'  │ '13101' │ '131010039' │ '100-0002' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │ '皇居外苑' │ 'コウキョガイエン' │    ''    │  ''   │  ''   │    ''    │ '' │  ''  │   ''   │  ''   │  ''   │
│    3    │ '100000100' │  '13'  │ '13101' │ '131010045' │ '100-0001' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │ '千代田'  │   'チヨダ'    │    ''    │  ''   │  ''   │    ''    │ '' │  ''  │   ''   │  ''   │  ''   │
│    4    │ '100000300' │  '13'  │ '13101' │ '131010051' │ '100-0003' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │ '一ツ橋'  │  'ヒトツバシ'   │    ''    │  ''   │ '１丁目' │ '０１チョウメ' │ '' │  ''  │   ''   │  ''   │  ''   │
│    5    │ '100000500' │  '13'  │ '13101' │ '131010055' │ '100-0005' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │ '丸の内'  │  'マルノウチ'   │    ''    │  ''   │  ''   │    ''    │ '' │  ''  │   ''   │  ''   │  ''   │
│    6    │ '100000600' │  '13'  │ '13101' │ '131010057' │ '100-0006' │  '0'   │  '0'  │ '東京都' │ 'トウキョウト' │ '千代田区' │ 'チヨダク' │ '有楽町'  │ 'ユウラクチョウ'  │    ''    │  ''   │  ''   │    ''    │ '' │  ''  │   ''   │  ''   │  ''   │
└─────────┴─────────────┴────────┴─────────┴─────────────┴────────────┴────────┴───────┴───────┴──────────┴────────┴────────┴────────┴────────────┴──────────┴───────┴───────┴──────────┴────┴──────┴────────┴───────┴───────┘
$ 
```

ちゃんとShift_JISのCSVを読み込めています。
また、CSVの1行目の**ヘッダ行の文字列をJSONデータのプロパティ名として扱えてます**ね。簡単です。


## おまけ: コマンドラインから使ってみる

npmに付属している``npx`` コマンドを使うことで、csvtojsonをコマンドラインから呼び出せます。

```console
$ npx csvtojson ./data/13tokyo.csv 
{"�Z��CD":"101841500","�s���{��CD":"13","�s�撬��CD":"13101","����CD":"131010019","�X�֔ԍ�":"101-8415","���Ə��t���O":"1","�
$
```

あー Shift_JISはダメですね。ということで``nkf`` をかましてみます。

```console
$ cat ./data/13tokyo.csv | nkf -S | npx csvtojson
{"住所CD":"101006101","都道府県CD":"13","市区町村CD":"13101","町域CD"
$
```

``nkf``コマンドは適宜[Homebrew](https://brew.sh/index_ja)などで入れておきましょう。
おつかれさまでした。


## 関連リンク・ソースコード

- https://github.com/masatomix/csv-sample-node.git ソース
- [住所.jp](http://jusyo.jp/csv/new.php) 
- [csvtojson](https://www.npmjs.com/package/csvtojson)
- [iconv-lite](https://www.npmjs.com/package/iconv-lite)
- [Homebrew](https://brew.sh/index_ja)