Bean単位での値の検証方法
CellProcessorは、フィールド1つに対する単項目チェックです。
項目間に対する相関チェックを行う場合は、CsvValidator
で1レコード分のBeanに対するチェック処理を実装します。
CsvValidatorの実装
CsvValidator
のメソッド validate(...)
を実装します。
エラー情報は、
CsvBindingErrors
に格納し管理されています。フィールドに対してエラーメッセージを追加する場合は、
CsvBindingErrors#rejectValue(...)
で追加します。項目間などのグローバルなエラーメッセージを追加する場合は、
CsvBindingErrors#reject(...)
で追加します。
CsvValidatorは、CellProcessorの後に実行され、もしエラーがある場合、そのフィールドの値は空が設定されており、
CsvBindingErrors#getFieldErrors(<フィールド名>)
でフィールドに対するエラーがあるかどうか判定する必要があります。既にエラーがある場合などの判定処理を巻単位するため、
CsvField
クラスを利用します。
1import java.util.Map;
2
3import com.github.mygreen.supercsv.validation.CsvBindingErrors;
4import com.github.mygreen.supercsv.validation.CsvField;
5import com.github.mygreen.supercsv.validation.CsvFieldValidator;
6import com.github.mygreen.supercsv.validation.CsvValidator;
7import com.github.mygreen.supercsv.validation.ValidationContext;
8
9// SampleCsvに対するValidator
10public class SampleValidator implements CsvValidator<SampleCsv> {
11
12 @Override
13 public void validate(final SampleCsv record, final CsvBindingErrors bindingErrors,
14 final ValidationContext<SampleCsv> validationContext) {
15
16 // フィールド ageの定義
17 final CsvField<Integer> ageField = new CsvField<>(bindingErrors, validationContext, record, "age");
18
19 // フィールド salaryの定義
20 final CsvField<Integer> salaryField = new CsvField<>(bindingErrors, validationContext, record, "salary");
21 salaryField
22 .add(new CsvFieldValidator<Integer>() {
23
24 @Override
25 public void validate(final CsvBindingErrors bindingErrors, final CsvField<Integer> field) {
26 if(ageField.isEmpty()) {
27 return;
28 }
29
30 // カラム「age(年齢)」が20以上の場合、カラム「給料(salary)」が設定されているかチェックする。
31 if(ageField.isNotEmpty() && ageField.getValue() >= 20 && field.isEmpty()) {
32 // メッセージ中の変数の作成
33 final Map<String, Object> vars = createMessageVariables(field);
34 vars.put("maxAge", 20);
35
36 // ageに関するフィールドエラーの追加
37 bindingErrors.rejectValue(field.getName(), field.getType(), "age.required", vars);
38 }
39 }
40 })
41 .add(new MaxValidator(10_000_000))
42 .validate(bindingErrors);
43
44
45 }
46
47 // CsvFieldValidator を別に実装
48 public static class MaxValidator implements CsvFieldValidator<Integer> {
49
50 private final int max;
51
52 public MaxValidator(final int max) {
53 this.max = max;
54 }
55
56 @Override
57 public void validate(CsvBindingErrors bindingErrors, CsvField<Integer> field) {
58 if(field.isEmpty()) {
59 return;
60 }
61
62 if(field.getValue() > max) {
63 // メッセージ変数の組み立て
64 Map<String, Object> vars = createMessageVariables(field);
65 vars.put("max", max);
66
67 bindingErrors.rejectValue(field.getName(), field.getType(), "fieldError.max", vars);
68 }
69 }
70
71 }
72
73}
CsvFieldValidator#createMessageVariables(...)
を利用すると、以下の良く使用するCSV情報に関する変数を作ることができます。
変数名 |
説明 |
---|---|
lineNumber |
CSVの実ファイル上の行番号。
カラムの値に改行が含まれている場合を考慮した実際の行番号なります。
1から始まります。
|
rowNumber |
CSVの論理上の行番号です。
1から始まります。
|
columnNumber |
CSVの列番号です。
1から始まります。
|
label |
@CsvColumn(label="<見出し>") 出指定したカラムの見出し名です。label属性を指定していない場合は、フィールド名になります。
|
validatedValue |
不正となった値です。
|
printer |
各フィールドの
TextFormatter のインスタンスです。${print#print(validatedValue)} でパース済みのオブジェクトをフォーマットするのに利用します。 |
作成したCsvValidatorは、 @CsvBean(validators=)
で指定します。
1import com.github.mygreen.supercsv.annotation.CsvBean;
2import com.github.mygreen.supercsv.annotation.CsvColumn;
3import com.github.mygreen.supercsv.annotation.constraint.CsvNumberMin;
4import com.github.mygreen.supercsv.annotation.constraint.CsvRequire;
5
6
7@CsvBean(header=true, validators=SampleValidator.class)
8public class SampleCsv {
9
10 @CsvColumn(number=1, label="名前")
11 @CsvRequire(considerBlank=true)
12 private String name;
13
14 @CsvColumn(number=2, label="年齢")
15 private Integer age;
16
17 @CsvColumn(number=3, label="給料")
18 @CsvNumberMin("0")
19 private Integer salary;
20
21 // setter/ getterは省略
22
23}
エラーメッセージの定義
CsvBindingErrors#reject(...)/rejectValue(...)
でエラーメッセージを追加する場合、エラーコードを指定します。
エラーメッセージは、クラスパスのルートに配置したプロパティファイル
SuperCsvMessages.properties
が自動的に読み込まれるため、そこに定義します。プロパティファイルは、UTF-8、ASCIIコード変換なしで作成します。 [ver.2.2]
メッセージキーは、「エラーコード」「クラス名」「フィールド名」「フィールドのクラスタイプ」を組み合わせて、優先順位の高いものに一致した物が採用されます。
メッセージ中では変数が利用可能で、予め利用可能な変数は下記が登録されています。
メッセージ変数は、
{key}
で参照可能です。
さらに、
${exp}
の形式だと、式言語として JEXL が利用可能です。ただし、 Spring Frameworkと連携してエラーメッセージの取得方法を変更 している場合は、定義する箇所は異なります。
優先度 |
形式 |
例 |
---|---|---|
1 |
|
age.reqired.SampleCsv.age
※「age.required」がエラーコード
|
2 |
|
age.reqired.age
※「age.required」がエラーコード
|
3 |
|
age.reqired.java.lang.Integer
※「age.required」がエラーコード
|
4 |
<エラーコード>.<フィールドのクラスタイプの親のクラスパス> ※数値型と列挙型のみ
|
age.reqired.java.lang.Number
age.reqired.java.lang.Enum
※「age.required」がエラーコード
|
5 |
|
age.reqired
※「age.required」がエラーコード
|
1###################################################
2# 独自のエラーメッセージの定義
3###################################################
4# 定義したキーは、再帰的に{キー名}で参照可能
5
6csvContext=[{rowNumber}行, {columnNumber}列]
7
8## エラーコードに対するメッセージの定義
9age.required={csvContext} : 項目「{label}」は、年齢が{maxAge}歳以上の場合には必須です。
10fieldError.max={csvContext} : 項目「{label}」の値(${printer.print(validatedValue)})は、${printer.print(max)}以内で入力してください。
エラーのハンドリング
CsvValidatorの中でCsvBindingErrorsにエラー情報を追加すると、例外 SuperCsvBindingException
としてスローされます。
例外クラスは、
CsvExceptionConverter
メッセージに変換します。CsvExceptionConverter は、
CsvAnnotationBeanReader/CsvAnnotationBeanWriter
に組み込まれているため、 メソッド#getErrorMessages()
で取得できます。詳細は、値の検証時のエラーメッセージ を参照してください。
1import java.nio.charset.Charset;
2import java.nio.file.Files;
3import java.io.File;
4import java.util.ArrayList;
5import java.util.List;
6
7import org.supercsv.prefs.CsvPreference;
8import org.supercsv.exception.SuperCsvException;
9
10import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
11import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
12
13
14public class Sample {
15
16 public void sampleRead() {
17
18 CsvAnnotationBeanReader<SampleCsv> csvReader;
19 try {
20 csvReader = new CsvAnnotationBeanReader<>(
21 SampleCsv.class,
22 Files.newBufferedReader(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
23 CsvPreference.STANDARD_PREFERENCE);
24
25 // ファイルの読み込み
26 List<SampleCsv> list = csvReader.readAll();
27
28 } catch(SuperCsvException e) {
29
30 // 変換されたエラーメッセージの取得
31 List<String> messages = csvReader.getErrorMessages();
32
33 } finally {
34 if(csvReader != null) {
35 csvReader.close();
36 }
37 }
38 }
39
40}