Super CSV AnnotationでCSVファイルを読み書きする

公式ドキュメント

Github
使用方法

Beanクラスの作成

クラスには@CsvBeanアノテーションを付与する。 各フィールドには@CsvColumnアノテーションを付与する。

その他作成するBeanクラスは以下のルールを満たしている必要がある。

  • publicなクラスとする
  • 引数なしコンストラクタを定義する(デフォルトコンストラクタでも可)
  • 各フィールドに対応するgetter/setterを作成する(lombokでも可)
import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;

import lombok.Data;

@Data
@CsvBean
public class Member {

  /** 名前 */
  @CsvColumn(number = 1)
  private String name;

  /** 住所 */
  @CsvColumn(number = 2)
  private String address;

  /** 年齢 */
  @CsvColumn(number = 3)
  private int age;

}

@CsvBeanの属性について

読み書きするCSVファイルにヘッダ行が存在する前提で処理をするかどうか。
trueを設定した場合、読み込み時は1行目をヘッダ行として扱い、書き込み時は1行目にヘッダ行を出力するようになる。

validateHeader

ヘッダー行の読み込み時に、値の検証を行うかどうか。
trueの場合、ヘッダー行の値が定義されている値と同じかどうか検証を行う。

検証の結果ヘッダーの値が不正な場合、com.github.mygreen.supercsv.exception.SuperCsvNoMatchHeaderExceptionがスローされる。

headerMapper

カラムに対するヘッダーラベルを取得方法を指定する。
デフォルトではカラムのラベル情報をヘッダーとする。

com.github.mygreen.supercsv.builder.HeaderMapperの実装クラスを指定する。

validators

レコードに対する値の検証を行うクラスを指定する。
カラム間の相関チェックやBean Validationを使用する際に設定する。

com.github.mygreen.supercsv.validation.CsvValidator<?>の実装クラスを指定する。
複数指定可能であり、指定した順にチェックが実行される。

listeners

ライフサイクルコールバック用のリスナークラスを指定するためのアノテーションを指定する。

@CsvColumnの属性について

number

CSVファイルから読み書きする際の列番号を指定する。
デフォルト値が設定されていて必須項目ではないように見えるが、列番号の重複は許可されていないので重複しないように値を設定する必要がある。
番号は1から設定する。

label

ヘッダに表示するカラム名を設定する。
未設定の場合はフィールド名が使用される。

builder

OSSがサポートしていないクラスにマッピングさせたい場合に使用する。 com.github.mygreen.supercsv.builder.ProcessorBuilderを実装したクラスを指定する。


CSVファイルの書き込み

com.github.mygreen.supercsv.io.CsvAnnotationBeanWriterクラスを使用してファイル書き込みを行う。
コンストラクタには、マッピングに使用するBeanクラス、Writer、CSV出力に係る各種設定を行うorg.supercsv.prefs.CsvPreferenceを指定する。

1行ずつ書き込む

1行ずつ書き込みを行う場合はwrite()メソッドを使用する。
このメソッドを使用する場合で、ヘッダ行を書き込みたい場合は、別途writeHeader()メソッドを呼び出す必要がある。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;

public class SampleCsvWriter {

  public void write() {
    try (FileWriter writer = new FileWriter("member.csv");
        BufferedWriter bw = new BufferedWriter(writer);
        CsvAnnotationBeanWriter<Member> csvWriter = new CsvAnnotationBeanWriter<>(
            Member.class,
            bw,
            CsvPreference.STANDARD_PREFERENCE);) {

      Member member = new Member();
      member.setName("太郎");
      member.setAddress("愛知県名古屋市");
      member.setAge(30);

      csvWriter.writeHeader();
      csvWriter.write(member);

      csvWriter.flush();

    } catch (IOException e) {
    }
  }
}

上記の実装で出力されるCSVファイルは以下の通り

name,address,age
太郎,愛知県名古屋市,30

改行を含むデータを設定しても出力することができる

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;

public class SampleCsvWriter {

  public void write() {
    try (FileWriter writer = new FileWriter("member.csv");
        BufferedWriter bw = new BufferedWriter(writer);
        CsvAnnotationBeanWriter<Member> csvWriter = new CsvAnnotationBeanWriter<>(
            Member.class,
            bw,
            CsvPreference.STANDARD_PREFERENCE);) {

      Member member = new Member();
      member.setName("太郎\\n山田");
      member.setAddress("愛知県名古屋市");
      member.setAge(30);

      csvWriter.writeHeader();
      csvWriter.write(member);

      csvWriter.flush();

    } catch (IOException e) {
    }
  }
}

上記の実装で出力されるCSVファイルは以下の通り

name,address,age
"太郎
山田",愛知県名古屋市,30

複数行まとめて書き込む

複数行まとめて書き込みを行う場合はwriteAll()メソッドを使用する。
@CsvBeanでheader属性にtrueが設定されている場合、ヘッダ行は自動で出力される。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;

public class SampleCsvWriter {

  public void write() {
    try (FileWriter writer = new FileWriter("member.csv");
        BufferedWriter bw = new BufferedWriter(writer);
        CsvAnnotationBeanWriter<Member> csvWriter = new CsvAnnotationBeanWriter<>(
            Member.class,
            bw,
            CsvPreference.STANDARD_PREFERENCE);) {

      Member member = new Member();
      member.setName("太郎\\n山田");
      member.setAddress("愛知県名古屋市");
      member.setAge(30);

      Member member2 = new Member();
      member2.setName("花子");
      member2.setAddress("愛知県長久手市");
      member2.setAge(45);

      List<Member> list = List.of(member, member2);

      csvWriter.writeAll(list);

      csvWriter.flush();

    } catch (IOException e) {
    }
  }
}

上記の実装で出力されるCSVファイルは以下の通り

name,address,age
"太郎
山田",愛知県名古屋市,30
花子,愛知県長久手市,45

CSVファイルの読み込み

com.github.mygreen.supercsv.io.CsvAnnotationBeanReader.CsvAnnotationBeanReaderクラスを使用してファイル書き込みを行う。
コンストラクタには、マッピングに使用するBeanクラス、Reader、CSV入力に係る各種設定を行うorg.supercsv.prefs.CsvPreferenceを指定する。

1行ずつ読み込む

1行ずつ読み込みを行う場合はread()メソッドを使用する。 読み込み対象のCSVは以下の通り

"name","address","age"
"太郎
山田","愛知県名古屋市","30"
"花子","愛知県長久手市","45"

データ行の読み込みの前にヘッダ行を読み込まないと例外(com.github.mygreen.supercsv.exception.SuperCsvBindingException)が発生する。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;
import org.supercsv.quote.AlwaysQuoteMode;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;

public class SampleCsvReader {

  public void read() {
    CsvPreference preference = new CsvPreference.Builder('"', ',', "\\n")
        .useQuoteMode(new AlwaysQuoteMode())
        .build();

    try (FileReader reader = new FileReader("member.csv");
        BufferedReader br = new BufferedReader(reader);
        CsvAnnotationBeanReader<Member> csvReader = new CsvAnnotationBeanReader<>(
            Member.class,
            br,
            preference);) {

      // ヘッダ行の読み込み
      String[] header = csvReader.getHeader(true);

      // データ行の読み込み
      Member member;
      while ((member = csvReader.read()) != null) {
        System.out.println(member);
      }
    } catch (IOException e) {
    }
  }
}

Streamで読み込む

Streamで読み込みを行う場合はlines()メソッドを使用する。
読み込み対象のCSVは以下の通り

"name","address","age"
"太郎
山田","愛知県名古屋市","30"
"花子","愛知県長久手市","45"

データ行の読み込みの前にヘッダ行を読み込まないと例外(com.github.mygreen.supercsv.exception.SuperCsvBindingException)が発生する。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;
import org.supercsv.quote.AlwaysQuoteMode;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;

public class SampleCsvReader {

  public void read() {
    CsvPreference preference = new CsvPreference.Builder('"', ',', "\\n")
        .useQuoteMode(new AlwaysQuoteMode())
        .build();

    try (FileReader reader = new FileReader("member.csv");
        BufferedReader br = new BufferedReader(reader);
        CsvAnnotationBeanReader<Member> csvReader = new CsvAnnotationBeanReader<>(
            Member.class,
            br,
            preference);) {

      // ヘッダー行の読み込み
      String headers[] = csvReader.getHeader(true);

      // データ行の読み込み
      csvReader.lines().forEach((member) -> {
        System.out.println(member);
      });
    } catch (IOException e) {
    }
  }
}

全行まとめて読み込む

複数行まとめて書き込みを行う場合はreadAll()メソッドを使用する。
@CsvBeanでheader属性にtrueが設定されている場合、1行目はヘッダとみなされる。

読み込み対象のCSVは以下の通り

"name","address","age"
"太郎山田","愛知県名古屋市","30"
"花子","愛知県長久手市","45"
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;
import org.supercsv.quote.AlwaysQuoteMode;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;

public class SampleCsvReader {

  public void read() {
    CsvPreference preference = new CsvPreference.Builder('"', ',', "\\n")
        .useQuoteMode(new AlwaysQuoteMode())
        .build();

    try (FileReader reader = new FileReader("member.csv");
        BufferedReader br = new BufferedReader(reader);
        CsvAnnotationBeanReader<Member> csvReader = new CsvAnnotationBeanReader<>(
            Member.class,
            br,
            preference);) {

      List<Member> list = csvReader.readAll();

      for (Member m : list) {
        System.out.println(m);
      }
    } catch (IOException e) {
    }
  }
}

org.supercsv.prefs.CsvPreferenceの設定

quoteChar

データ内に区切り文字や改行コードがあった時にデータを囲う文字を指定する。
delimiterCharと異なる値を設定する。

delimiterChar

カラムの区切り文字を設定する。
quoteCharと異なる値を設定する。

endOfLineSymbols

改行コードを設定する。

surroundingSpacesNeedQuotes

セルの先頭または末尾にあるスペースが引用符で囲まれていない場合、無視するかどうか(CSVの読み込みと書き込みの両方に適用可能)。
trueを設定した場合、無視する。
デフォルト:false

ignoreEmptyLines

空行(すなわち、行末記号のみを含む)を無視するかどうか。
trueを設定した場合、無視する。
デフォルト:true

encoder

カスタムCsvEncoderを使用して、書き込む際にCSVエスケープする。org.supercsv.encoder.CsvEncoderの実装クラスを指定する。

quoteMode

独自のQuoteModeを書き込み時に適用する(カラムに特殊文字が含まれておらず、クォートが適用されない場合のみ適用される)。

独自のQuoteModeを用意して設定するか、 定義済みのものを使用する。

  • AlwaysQuoteMode
    AlwaysQuoteModeを使用する場合、常に引用符で囲む設定が適用される
  • ColumnQuoteMode
    ColumnQuoteModeを使用する場合、特殊文字エスケープに必要な場合、または特定の列が常に引用符で囲むべきな場合に引用符で囲む。

org.supercsv.quote.QuoteModeの実装クラスを指定する。

commentMatcher

コメントをスキップする機能を有効にする。
独自のCommentMatcherを用意して設定するか、 定義済みのものを使用する。

  • CommentStartsWith
    指定したStringで始まる行にマッチするCommentMatcher
    指定した文字列で始まる行をコメントとみなす
  • CommentMatches
    指定された正規表現に合致する行にマッチする CommentMatcher
    指定された正規表現に合致する行をコメントとみなす

org.supercsv.comment.CommentMatcherを実装したクラスを指定する。

maxLinesPerRow

例外が発生する前に行がまたがることができる最大行数 (CSVを読み込み時のみ適用可能)。

このオプションは、引用符が対応しないCSVに遭遇したときに、CSVリーダが高速に失敗することを可能とする。

通常の動作では、対応する引用符が見つかるまで読み込みを続けるが、これはファイル全体を読み込むことになる可能性があり、利用可能なメモリをすべて使い果たすかもしれない。

0または負の値を指定すると、無効となる。   デフォルト: 0


quoteChar, delimiterChar, endOfLineSymbolsについて設定されたものが予め4種類定数で用意されているので、ニーズが合えばそれを使用しても良い。

定数名 quoteChar delimiterChar endOfLineSymbols
STANDARD_PREFERENCE " , \r\n
EXCEL_PREFERENCE " , \n
EXCEL_NORTH_EUROPE_PREFERENCE " ; \n
TAB_PREFERENCE " \t \n

以下にCsvPreferenceの設定を定数を使用せずにした場合の実装サンプルを示す。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.supercsv.prefs.CsvPreference;
import org.supercsv.quote.AlwaysQuoteMode;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;

public class SampleCsvWriter {

  public void write() {
    CsvPreference preference = new CsvPreference.Builder('"', ',', "\\n")
        .useQuoteMode(new AlwaysQuoteMode())
        .build();

    try (FileWriter writer = new FileWriter("member.csv");
        BufferedWriter bw = new BufferedWriter(writer);
        CsvAnnotationBeanWriter<Member> csvWriter = new CsvAnnotationBeanWriter<>(
            Member.class,
            bw,
            preference);) {

      Member member = new Member();
      member.setName("太郎\\n山田");
      member.setAddress("愛知県名古屋市");
      member.setAge(30);

      Member member2 = new Member();
      member2.setName("花子");
      member2.setAddress("愛知県長久手市");
      member2.setAge(45);

      List<Member> list = List.of(member, member2);

      csvWriter.writeAll(list);

      csvWriter.flush();

    } catch (IOException e) {
    }
  }
}

上記の実装で出力されるCSVファイルは以下の通り

"name","address","age"
"太郎
山田","愛知県名古屋市","30"
"花子","愛知県長久手市","45"

CSVファイルのうち一部のカラムを除外して読み書きしたい

Beanクラスに@CsvPartialアノテーションを付与することで実現可能。

import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;
import com.github.mygreen.supercsv.annotation.CsvPartial;

import lombok.Data;

@Data
@CsvBean(header = true)
@CsvPartial(columnSize = 5, headers = {
    @CsvPartial.Header(number = 3, label = "電話番号"),
    @CsvPartial.Header(number = 5, label = "生年月日") })
public class SampleCsv {

  // 読み書きしないフィールドは宣言しない

  @CsvColumn(number = 1, label = "ID")
  private int id;

  @CsvColumn(number = 2, label = "氏名")
  private String name;

  @CsvColumn(number = 4, label = "メールアドレス")
  private String email;

}

@CsvPartialの属性

columnSize

実際のCSVファイルのカラム数を指定する。   フィールドの@CsvColumnのnumber属性の値よりも大きい値を設定する。

headers

Beanに定義されていないカラムのヘッダー情報を設定する。   ヘッダ情報には以下の2つが設定可能

  • カラム番号
  • ヘッダのラベル

ファイルの読み書きを実装する

読み込み対象のCSVは以下の通り

"ID","氏名","電話番号","メールアドレス","生年月日"
"1","山田","09012345678","sample@co.jp","19991212"

ファイルの読み書きの実装方法は、除外カラムの有無にかかわらず同一である。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import org.supercsv.prefs.CsvPreference;
import org.supercsv.quote.AlwaysQuoteMode;

import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;

public class SampleCsvOperation {
  void partial() {
    CsvPreference preference = new CsvPreference.Builder('"', ',', "\\n")
        .useQuoteMode(new AlwaysQuoteMode())
        .build();

    // 一部カラムを除外したファイル読み込み
    try (FileReader reader = new FileReader("partial_test.csv");
        BufferedReader br = new BufferedReader(reader);
        CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
            SampleCsv.class,
            br,
            preference);) {

      // ヘッダー行の読み込み
      String headers[] = csvReader.getHeader(true);

      // データ行の読み込み
      csvReader.lines().forEach((member) -> {
        System.out.println(member);
      });

    } catch (IOException e) {
    }

    // 一部カラムを除外したファイル書き込み
    try (FileWriter writer = new FileWriter("partial_test_write.csv");
        BufferedWriter bw = new BufferedWriter(writer);
        CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
            SampleCsv.class,
            bw,
            preference);) {

      SampleCsv csv = new SampleCsv();
      csv.setId(2);
      csv.setName("ほげ");
      csv.setEmail("hoho@co.jp");

      csvWriter.writeHeader();
      csvWriter.write(csv);

      csvWriter.flush();

    } catch (IOException e) {
    }
  }
}

上記の実装で出力されるCSVファイルは以下の通り

"ID","氏名","電話番号","メールアドレス","生年月日"
"2","ほげ",,"hoho@co.jp",

書式を指定するアノテーション

@CsvBooleanFormat

クラスタイプが「boolean/Boolean」のマッピング規則を定義する際に使用する。

属性には以下のものがある。

  • readForTrue 読み込むときにtrueと判定する文字列の配列
  • readForFalse 読み込むときにfalseと判定する文字列の配列
  • ignoreCase 読み込むときに大文字・小文字を無視するかどうか
  • failToFalse 読み込むときに「readForTrue」「readForFalse」にない値をfalseとして読み込むかどうか
  • message パースに失敗した時のメッセージ
  • writeAsTrue 書き込むときにtrueを表現する値
  • writeAsFalse 書き込むときにfalseを表現する値
@Data
@CsvBean(header = true)
public class Member {

  /** フラグ */
  @CsvColumn(number = 4)
  @CsvBooleanFormat(readForTrue = { "○", "有効", "レ" },
                    readForFalse = { "×", "無効", "-", "" },
                    writeAsTrue = "○",
                    writeAsFalse = "×",
                    ignoreCase = true,
                    failToFalse = true,
                    message = "パースに失敗した!")
  private boolean flag;

}

@CsvNumberFormat

数値型に対する書式を指定する際に使用する。

対応するJavaクラスタイプは以下の通り

  • byte/short/int/long/float/double のプリミティブ型とそのラッパークラス
  • java.math.BigDecimal / java.math.BigInteger の数値クラス

プリミティブ型に対して読み込む際に、CSVのカラムの値が空の場合、それぞれのプリミティブ型の初期値が設定される。

属性には以下のものがある。

  • pattern
    数値の書式を指定する
    指定しない場合、クラスごとの標準の書式を使用する
  • lenient
    読み込み時に数値の解析を曖昧に行うか
    trueの場合、曖昧な解析を行う(デフォルト:false)
  • currency
    通貨コードを指定する
    java.util.Currencyで解釈可能なコードを指定
    pattern指定時のみ有効
  • locale
    ロケールを指定する
    省略した場合、システム標準の値を使用する
  • rounding
    丸め方の方法を指定する
    java.math.RoundingModeの値を設定する
  • precision
    丸めの精度を指定する
    pattern未指定時のみ有効
    主に小数の場合に有効桁数を揃える際に使用する
  • message
    パースに失敗した時のメッセージ

@CsvDateTimeFormat

日時型に対する書式を指定する際に使用する。
アノテーションを付与していないときや属性 pattern を指定しないときは、クラスタイプごとに決まった標準の書式が適用される。

標準の書式はここを参照。

属性には以下のものがある。

  • pattern 日時の書式を指定する
    指定しない場合、クラスごとの標準の書式を使用する
  • lenient
    読み込み時に日時の解析を曖昧に行うか
    trueの場合、曖昧な解析を行う(デフォルト:false)
  • timezone
    タイムゾーンを指定する
    省略した場合、システム標準の値を使用する
  • locale
    ロケールを指定する
    省略した場合、システム標準の値を使用する
  • message
    パースに失敗した時のメッセージ
@Data
@CsvBean(header = true)
public class Member {

  /** 日時 */
  @CsvColumn(number = 5)
  @CsvDateTimeFormat(pattern = "uuuu/MM/dd HH:mm:ss")
  private LocalDateTime datetime;

}

@CsvEnumFormat

Enumの変換規則の設定を行う際に使用する。

アノテーション @CsvEnumFormat を付与しなくてもマッピングは可能。
マッピングには、カラムの値とEnumの要素の値(Enum#name())が使用される。

属性には以下のものがある。

  • ignoreCase
    読み込むときに大文字・小文字を無視するかどうか
  • selector
    Enum#name()の値とは別の値でマッピングさせたい場合に、マッピングに使用するメソッド名を指定する
    メソッドは引数なしの文字列型を返すものが指定可能
  • message
    パースに失敗した時のメッセージ

selectorを指定したマッピングの書き方

マッピングしたいEnum

public enum RoleType {

  Normal("一般権限"),
  Admin("管理者権限");

  private String localeName;

  private RoleType(String localeName) {
    this.localeName = localeName;
  }

  public String localeName() {
    return this.localeName;
  }

}

Beanクラス

@Data
@CsvBean(header = true)
public class Member {

  /** Enum */
  @CsvColumn(number = 6)
  @CsvEnumFormat(ignoreCase = true, selector = "localeName")
  private RoleType role;

}