2010/04/27

CSVファイルの読込

社内の受託案件で、以前作ったメール送信用のアプリを使いたいという要望があり、隙間の時間で少しずつカスタマイズしてます。その中で送信先のユーザー情報をCSVで一括取込したいというのがあったので、簡単にCSVを任意のBeanのListに変換するようなクラスを作ってみました。この機能はアノテーションを使うのでJava5以上が必要となります。

1. アノテーション
まずはBeanのフィールドに指定するアノテーションです。CSVを読み込んだときに、このアノテーションが宣言されているフィールドに対して、値を設定します。
/**
 * CSVデータの保持対象を表すアノテーションです。
 * このアノテーションはビーンクラスのフィールドに対して宣言します。
 * 
 * @author namiki
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface CsvColumns {
 /**
  * CSVのカラム順を指定します。
  * 
  * @return カラム順
  */
 int index();
}
indexでCSVの何カラム目を設定するか、指定します。

2. 任意のBean
続いてCSVの内容を設定するBeanです。
public class Entity {
 @CsvColumns(index = 0)
 public Integer id;

 @CsvColumns(index = 2)
 public String name;

 @CsvColumns(index = 1)
 public String email;
}
Seasar2には依存していませんが、基本的にSeasar2環境で使うことを想定しているため、publicフィールドを採用しています。

3. CSVParser
最後にCSVを読み込むためのクラスです。
/**
 * CSVファイルを読み込み、型パラメータで指定されたクラスのリストを作成するクラスです。
 * 
 * @author namiki
 * 
 */
public class CsvFileParser<T> {

    // ------------------------------------------------------------- [Constants]

    /** CSVの区切り文字です。 */
    private static final String DELIMITER = ",";

    // ------------------------------------------------------------ [Properties]

    /** 読込対象ファイルです。 */
    private File target;

    // ----------------------------------------------------------- [Constructor]

    /**
     * コンストラクタです。
     * 
     * @param target
     *            対象ファイル
     */
    public DcssCsvFileParser(File target) {
        this.target = target;
    }

    // -------------------------------------------------------- [Public methods]

    /**
     * CSVファイルを読込み、型パラメータで指定されたクラスのリストを返します。
     * 
     * @param c
     *            型
     * @return リスト
     */
    @SuppressWarnings("unchecked")
    public List<T> readLines(Class<T> c) {

        List<Object> list = new ArrayList<Object>();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(target));
            String line = null;

            while ((line = reader.readLine()) != null) {
                String[] array = line.split(DELIMITER);

                Object entity = c.newInstance();
                Field[] fields = c.getFields();
                for (Field field : fields) {
                    CsvColumns col = field.getAnnotation(CsvColumns.class);
                    if (null != col) {
                        if (field.getType() == Integer.class) {
                            field.set(entity, Integer
                                    .valueOf(array[col.index()]));
                        } else if (field.getType() == Long.class) {
                            field.set(entity, Long.valueOf(array[col.index()]));
                        } else {
                            field.set(entity, array[col.index()]);
                        }
                    }
                }
                list.add(entity);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);

        } finally {
            IOUtils.closeQuietly(reader);
        }
        return (List<T>) list;
    }
}

4. 使い方
最後にこのクラスの使い方です。
CsvFileParser parser = new CsvFileParser(file);
List list = parser.readLines(Entity.class);
GenericsやらClass指定やらが続いて、少し気持ち悪いですね…。ちょっとここら辺は直したいところですが、取り敢えずこんな感じでCSV読み込みを簡略化することができました。
ただあまりCSVの行数が多いとOutOfMemoryになってしまいそうなので注意が必要ですね。そこら辺も合わせてもう少し手を入れていこうと思います。

0 件のコメント:

コメントを投稿