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になってしまいそうなので注意が必要ですね。そこら辺も合わせてもう少し手を入れていこうと思います。

2010/04/23

またまた本を買う

休みの日にTVを見てたら村上春樹の「1Q84」のBook3が出版されたと大騒ぎになってました。
なんとなく騒がれてたのは知ってましたが、「1Q84」は読んでなかったのでこれを機に購入。残念ながらBook3は売り切れていましたが、Book1、Book2を読み終えた頃には買えるだろうと勝手に予測してます。
取り敢えずBook1は読了してBook2に突入していますが、読んでいるとなんとも不思議な感覚に陥ってしまいますね。思わず月を見上げたり、ソファの下を覗き込んだりしてしまいました。

あと、Amazonから「Seasar2徹底入門」が届きました。相変わらずこの方の文章は読み易く、分かり易いです。SAStrutsに関しては最近関わったプロジェクト群でよく使っていますが、まだまだ奥が深いです。自分自身この本で初めて知ったことなどもありますので、これをベースにまた社内用資料を作成していこうと思っています。