JavaでCSVの読み込み(その5)
welcome-to-the-world.hatenablog.jp
今回はCSVファイルの読み込み部分(ベース)を実装しようと思います。
上記メソッドをさらにオーバーロードして一般的なCSVを読み込むメソッドも実装します。
「一般的なCSVを読み込む」とは今回は下記の条件を満たすこととします。
- 区切り文字はカンマ
- 囲み文字はダブルクォーテーション
- 改行も囲み文字に囲まれていれば1項目として読み込む
今回はまず、ベースとなるCSVを読み込むメソッドを実装します。
CSVファイルの読み込みには「commons-csv-1.8」を使用します。
また、戻り値のList(Bean)を返すために「commons-beanutils-1.9.4」を使用します。
「commons-beanutils-1.9.4」を使用する際に必要な
- commons-collections4-4.4
- commons-logging-1.2
もインポートしておきます。
上記クラスはいずれも「Apache Commons」からダウンロード可能です。
ベースとなるCSVを読み込むメソッドは「commons-csv-1.8」のCSVFormatクラスを引数として、色々な条件を指定できるようにします。
実際のコードは下記の通りです。
/** * CSVの読み込み * * @param file Fileオブジェクト * @param bean Bean * @param charSets 文字コード * @param format CSVFormat * @return Beanのリスト * @throws ClassNotFoundException * @throws CsvUtilsException */ public static List<?> readCsv(File file, Object bean, String charSets, CSVFormat format) throws CsvUtilsException { // 引数beanのフィールド名の一覧を取得 List<string> beanFields = CsvUtils.getFieldNameList(bean); // 戻り値のListを初期化 List<object> beanList = new ArrayList<object>(); if (file.exists()) { // ファイルが存在すればオープンしてBufferedReaderに渡す try(FileInputStream fileInputStream = new FileInputStream(file); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, charSets); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { // CSVの読み込み CSVParser parser = format.parse(bufferedReader); // 読み込んだレコード数ループ for (CSVRecord record : parser) { // フィールド数をチェック if (beanFields.size() != record.size()) { // TODO:例外処理 } // MAPに項目名+値をペアで設定 HashMap<String, String> csvFieldMap = new HashMap<String, String>(); for (int i=0;i<record.size();i++) { // 項目名+値 csvFieldMap.put(beanFields.get(i), record.get(i)); } // 引数のBeanのクラス名からクラスを取得 Class<?> classBean = Class.forName(bean.getClass().getName()); // 取得したクラスからインスタンスを生成 Object objectBean = classBean.newInstance(); // MAPの項目名からBeanの該当する項目に値を設定 BeanUtils.populate(objectBean, csvFieldMap); // 戻り値にBeanを設定 beanList.add(objectBean); } } catch(Exception e) { // TODO:例外処理 throw new CsvUtilsException(e); } finally { // クローズ不要 } } else { // TODO:例外処理 } return beanList; }
16行目でCSVで読み込んだ値とBeanの項目をマッピングするためにBeanの項目の一覧を取得しています。(その3で作成したメソッドです)
18行目で戻り値のList(Bean)を初期化しています。
戻り値の宣言はBeanに対応するためList<?>としています。
20行目でファイルの存在チェックをしています。
今回は引数でFileクラスで受け取るようにしています。
22~24行目でファイルのオープンをしています。
「commons-csv-1.8」にBufferedReaderを渡したいのでBufferedReaderでファイルを読み込んでいます。
また、ファイルオープンをtryで括ってファイルクローズを省略しています。
27行目で 「commons-csv-1.8」のCSVParserクラス、引数のCSVFormatクラスを利用してCSVファイルを解析しています。
解析されたCSVファイルは CSVFormatクラスの条件を元に1行のCSVRecordクラスに分解されます。
CSVRecordクラスはCSVファイルの各項目の情報を保持しています。
30~50行目でCSVファイルの行数分ループして、1行の情報を戻り値のList(Bean)に詰めています。
32~34行目で 16行目で取得したBeanの項目数とCSVRecordクラスの項目数をチェックしています。(一致しない場合は例外をスローする予定です)
37行目~47行目で 「commons-beanutils-1.9.4」を使用してBeanにマッピングするために、いったん1行(Bean)の情報をMAPに詰めています。
そのMAPを「commons-beanutils-1.9.4」のBeanUtilsのpopulateメソッドでBeanにマッピングしています。(47行目)
マッピングしたBeanを戻り値のListに49行目で詰めています。
43行目~45行目で引数のBeanのクラス情報を元にBeanのインスタンスを生成して47行目のマッピングに使用しています。
ざっくりとした説明で済みません^^;
引数のBeanの型が不明なのでこのような回りくどい方法でBeanにCSVファイルの解析結果を詰め込んでListで返しています。
例外処理については独自の例外クラスを作成して別途実装しようと思っています。
次回は「一般的なCSVを読み込む」を実装しようと思います。
その点はご了承ください。