5. 値の検証方法
5.1. 値の検証用の既存のアノテーション
既存のアノテーションとして、以下のものが用意されています。
アノテーション |
概要 |
参照 |
---|---|---|
|
必須チェックを行います。 |
|
|
指定した値と等しいか検証します。 |
|
|
他のレコードの値と異なるか検証します。 |
|
|
他のレコードの値と異なるかハッシュコードにより検証します。 |
アノテーション |
概要 |
参照 |
---|---|---|
|
正規表現と一致するか検証します。 |
|
|
指定した文字長以上か検証します。 |
|
|
指定した文字長以内か検証します。 |
|
|
指定した文字長の範囲内か検証します。 |
|
|
指定した文字長か検証します。 |
|
|
指定した文字長か検証します。 |
|
|
指定した語彙を含んでいないか検証します。 |
|
|
指定した語彙を含んでいるか検証します。 |
アノテーション |
概要 |
参照 |
---|---|---|
|
指定した下限値以上か検証します。 |
|
|
指定した上限値以下か検証します。 |
|
|
指定した値の範囲内か検証します。 |
アノテーション |
概要 |
参照 |
---|---|---|
|
指定した値以降(下限値以上)か検証します。 |
|
|
指定した値以前(上限値以下)か検証します。 |
|
|
指定した値の期間内か検証します。 |
5.1.1. 処理順序の指定
属性 order
で処理順序を指定することができます。
値が大きいほど後から実行されます。
値が同じ場合は、アノテーションのFQCN(完全限定クラス名)の昇順で実行されます。
属性orderを省略した場合は、デフォルト値
0
が適用されます。ただし、必須チェック用の
@CsvRequire
は、初めに実行されるよう、属性orderのデフォルト値にはIntegerの最小値(-2147483648)が設定されています。
読み込み時、書き込み時とも同じ処理順序になります。
属性
order
が付与されていないアノテーションは順番が付与されているものよりも後になります。
1import com.github.mygreen.supercsv.annotation.CsvBean;
2import com.github.mygreen.supercsv.annotation.CsvColumn;
3
4import com.github.mygreen.supercsv.annotation.constraint.*;
5
6@CsvBean
7public class SampleCsv {
8
9 @CsvColumn(number=1)
10 @CsvRequire
11 @CsvUnique(order=2)
12 @CsvNumberMin(value="0", order=3)
13 private Integer value;
14
15 // getter/setterは省略
16}
5.1.2. 処理ケースの指定
属性 cases
で、アノテーションを適用するケースとして「読み込み時」「書き込み時」を限定することができます。
列挙型
BuildCase
で指定します。BuildCase.Read
が読み込み時、BuildCase.Write
が書き込み時を表します。
属性の値が空(配列が空)の場合、または、属性 cases を指定しない場合は、全てのケースに該当します。
既存のアノテーションは、基本的に全て属性値が空が設定され、全てのケースに該当します。
1import com.github.mygreen.supercsv.annotation.CsvBean;
2import com.github.mygreen.supercsv.annotation.CsvColumn;
3import com.github.mygreen.supercsv.annotation.constraint.*;
4import com.github.mygreen.supercsv.builder.BuildCase;
5
6@CsvBean
7public class SampleCsv {
8
9 // 空白の場合、トリミングして空文字となった場合に入力値なしと判断して、nullに変換します。
10 @CsvColumn(number=1)
11 @CsvLengthMax(value=10, cases={}) // 全てのケースに適用
12 @CsvLengthMin(value=0, cases=BuildCase.Read) // 読み込み時のみ適用
13 @CsvUnique(cases=BuildCase.Write) // 書き込み時のみ適用
14 private String comment;
15
16 // getter/setterは省略
17}
5.1.3. グループの指定
属性 groups
で、グループ用クラスを指定することで、属性 cases より柔軟に適用するケースをを限定することができます。
Bean Validation のgroupと同じような考え方ですが、適用される順序は関係ありません。
本ライブラリでは、順序を指定したいときは、属性 order を指定します。
属性を指定しない(空の)場合は、デフォルトのグループ
com.github.mygreen.supercsv.annotation.DefaultGroup
が適用されたと同じ意味になります。Bean Validationのデフォルトグループ
javax.validation.groups.Default
とは異なるため、特にBeanValidationのアノテーションと混在させる場合は注意してください。
グループ用クラスは、実装が必要ないため、通常はインタフェースで作成します。
1import com.github.mygreen.supercsv.annotation.CsvBean;
2import com.github.mygreen.supercsv.annotation.CsvColumn;
3import com.github.mygreen.supercsv.annotation.DefaultGroup;
4import com.github.mygreen.supercsv.annotation.constraint.*;
5
6@CsvBean
7public class SampleCsv {
8
9 @CsvColumn(number=1)
10 @CsvRequire
11 @CsvNumberMin(value="0", groups=AdminGroup.class, order=2)
12 @CsvNumberMax(value="100", groups=NormalGroup.class, order=2)
13 private Integer value;
14
15 // getter/setterは省略
16}
17
18// グループ用クラスの作成
19public static interface AdminGroup {}
20public static interface NormalGroup {}
実行時は、CsvAnnotationBeanReader/CsvAnnotationBeanWriter/BeanMappingFactory
の引数で指定します。
1import com.github.mygreen.supercsv.builder.BeanMapping;
2import com.github.mygreen.supercsv.builder.BeanMappingFactory;
3import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
4import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;
5
6import java.nio.charset.Charset;
7import java.nio.file.Files;
8import java.io.File;
9import java.util.ArrayList;
10import java.util.List;
11
12import org.supercsv.prefs.CsvPreference;
13
14public class Sample {
15
16 // 読み込み時のグループの指定
17 public void sampleRead() {
18
19 CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
20 SampleCsv.class,
21 Files.newBufferedReader(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
22 CsvPreference.STANDARD_PREFERENCE,
23 DefaultGroup.class, AdminGroup.class); // デフォルトとAdmin用のグループクラスを指定する。
24
25 //... 以下省略
26
27 }
28
29 // 書き込み時のグループの指定
30 public void sampleWrite() {
31
32 CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
33 SampleCsv.class,
34 Files.newBufferedWriter(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
35 CsvPreference.STANDARD_PREFERENCE,
36 DefaultGroup.class, NoramlGroup.class); // デフォルトとNoraml用のグループクラスを指定する。
37
38 //... 以下省略
39
40 }
41
42 // BeanMapping作成時の指定
43 public void sampleBeanMapping() {
44
45 // BeanMappingの作成
46 BeanMappingFactory mappingFactory = new BeanMappingFactory();
47 BeanMapping<SampleCsv> beanMapping = mappingFactory.create(SampleCsv.class,
48 DefaultGroup.class, NoramlGroup.class); // デフォルトとNormal用のグループクラスを指定する。
49
50 CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
51 beanMapping,
52 Files.newBufferedReader(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
53 CsvPreference.STANDARD_PREFERENCE);
54
55 //... 以下省略
56 }
57
58}
5.2. 独自の検証処理の作成方法
独自の検証処理を実装するには、3つのステップを踏む必要があります。
CellProcessorの実装クラスの作成
アノテーションクラスの作成
CellProcessorを作成するためのファクトリクラスの作成
以下、それぞれに対して解説していきます。
5.2.1. CellProcessorの実装クラスの作成
サンプルとして、最後が任意の文字で終わるか検証するCellProcessorを作成します。
抽象クラス
ValidationCellProcessor
[ JavaDoc ] を継承して作成します。ValidationCellProcessor は、値の検証に特化したCellProcessorの実装です。
CellProcessorは、「Chain of Responsibility」パターンであるため、その構造を表現するためのクラスとなります。
今回は、文字列型の値を検証するため、インタフェースとして
StringCellProcessor
[ JavaDoc ] を実装します。この実装は特に必要ないですが、扱うカラムの値の種類を表現するめのものです。 CellProcessorを直接組み立てる時に、これらのインタフェースでchainとして追加する次のCellProcessorを限定するために使用します。
扱う値が数値型のときは
LongCellProcessor
[ JavaDoc ]などと、扱う値によって変更してください。
コンストラクタとして、chainの次の処理となるCellProcessorを引数に持つものと、持たないものを必ず2つ実装します。
メソッド
execute(...)
内で処理の実装を行います。nullの場合、次の処理に委譲するようにします。 Super CSVの既存のCellProcessorではメソッドvalidateInputNotNull(...)を呼びnullチェックを行いますが、 本ライブラリではnullに対する処理は他のCellProcessorで行うため、次の処理に渡します。
検証対象のクラスタイプが不正な場合は、例外
SuperCsvCellProcessorException
をスローします。 アノテーションを間違った型に付与した場合に発生する場合がありますが、ファクトリクラスの段階で弾いてもかまいません。正常な値であれば、次の処理に渡します。
問題がある場合、例外
SuperCsvValidationException
をスローします。 その際に、メソッド createValidationException(...) を呼び出して、ビルダクラスを利用して例外クラスを組み立てます。
1import org.supercsv.cellprocessor.ift.CellProcessor;
2import org.supercsv.cellprocessor.ift.StringCellProcessor;
3import org.supercsv.util.CsvContext;
4
5import com.github.mygreen.supercsv.cellprocessor.ValidationCellProcessor;
6
7// 独自の値の検証用のCellProcessor
8public class CustomConstraint extends ValidationCellProcessor
9 implements StringCellProcessor {
10
11 private String text;
12
13 public CustomConstraint(final String text) {
14 super();
15 checkPreconditions(text);
16 this.text = text;
17 }
18
19 public CustomConstraint(final String text, final CellProcessor next) {
20 super(next);
21 checkPreconditions(text);
22 this.text = text;
23 }
24
25 // コンストラクタで渡した独自の引数のチェック処理
26 private static void checkPreconditions(final String text) {
27 if(text == null) {
28 throw new NullPointerException("text should not be null.");
29 } else if(text.isEmpty()) {
30 throw new NullPointerException("text should not be empty.");
31 }
32 }
33
34 @Override
35 public <T> T execute(final Object value, final CsvContext context) {
36 if(value == null) {
37 // nullの場合、次の処理に委譲します。
38 return next.execute(value, context);
39 }
40
41 final String result;
42 if(value instanceof String) {
43 result = (String)value;
44
45 } else {
46 // 検証対象のクラスタイプが不正な場合
47 throw new SuperCsvCellProcessorException(String.class, value, context, this);
48 }
49
50 // 最後が指定した値で終了するかどうか
51 if(result.endsWith(text)) {
52 // 正常な値の場合、次の処理に委譲します。
53 return next.execute(value, context);
54 }
55
56 // エラーがある場合、例外クラスを組み立てます。
57 throw createValidationException(context)
58 .messageFormat("Not ends with %s.", text)
59 .messageVariables("suffix", text)
60 .build();
61
62 }
63
64}
5.2.2. 値の検証用のアノテーションクラスの作成
@Target
として、ElementType.FIELD
とElementType.ANNOTATION_TYPE
の2つを指定します。通常はFieldのみで問題ないですが、 アノテーションを合成 するときがあるため、 ANNOTATION_TYPE も追加しておきます。
@Repeatable
として、複数のアノテーションを設定できるようにします。内部アノテーションとして、 List を定義します。
値の検証用のアノテーションであることを示すためのメタアノテーション
@CsvConstraint
[ JavaDoc ]を指定します。属性
value
に、ConstraintProcessorFactory
[ JavaDoc ]を実装したCellProcessorのファクトリクラスの実装を指定します。
共通の属性として、
cases
とgroups
、order
を定義します。省略した場合は、それぞれのデフォルト値が適用されます。
必要であれば、固有の属性を定義します。今回は、
text
を定義します。これはCellProcessorに渡す値となります。
1import java.lang.annotation.Documented;
2import java.lang.annotation.ElementType;
3import java.lang.annotation.Repeatable;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8import com.github.mygreen.supercsv.annotation.constraint.CsvConstraint;
9import com.github.mygreen.supercsv.builder.BuildCase;
10
11
12// 独自の値の検証用のアノテーション
13@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
14@Retention(RetentionPolicy.RUNTIME)
15@Documented
16@Repeatable(CsvCustomConstraint.List.class)
17@CsvConstraint(CustomConstratinFactory.class) // ファクトリクラスを指定
18public @interface CsvCustomConstraint {
19
20 // 固有の属性 - チェックすることとなる最後の文字を指定します。
21 String text();
22
23 // 共通の属性 - ケース
24 BuildCase[] cases() default {};
25
26 // 共通の属性 - グループ
27 Class<?>[] groups() default {};
28
29 // 共通の属性 - 並び順
30 int order() default 0;
31
32 // 繰り返しのアノテーションの格納用アノテーションの定義
33 @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
34 @Retention(RetentionPolicy.RUNTIME)
35 @Documented
36 @interface List {
37
38 CsvCustomConstraint[] value();
39 }
40}
5.2.3. 値の検証用のファクトリクラスの作成
アノテーションをハンドリングして、CellProcessorを作成するためのファクトリクラスを作成します。
インタフェース
ConstraintProcessorFactory
[ JavaDoc ]を実装します。アノテーションが検証対象のクラスタイプ以外に付与される場合があるため、その際は無視するようにします。
独自のCellProcessorのCustomConstraintのインスタンスを作成します。
Chainの次の処理となるCellProcessorの変数「next」は、空であることがあるため、コンストラクタで分けます。
1import com.github.mygreen.supercsv.builder.BuildType;
2import com.github.mygreen.supercsv.builder.Configuration;
3import com.github.mygreen.supercsv.builder.FieldAccessor;
4import com.github.mygreen.supercsv.cellprocessor.ConstraintProcessorFactory;
5import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
6
7public class CustomConstraintFactory implements ConstraintProcessorFactory<CsvCustomConstraint> {
8
9 @Override
10 public Optional<CellProcessor> create(CsvCustomConstraint anno, Optional<CellProcessor> next,
11 FieldAccessor field, TextFormatter<?> formatter, Configuration config) {
12
13 if(!String.class.isAssignableFrom(field.getType())) {
14 // 検証対象のクラスタイプと一致しない場合は、弾きます。
15 return next;
16 }
17
18 // CellProcessorのインスタンスを作成します
19 final CustomConstraint processor = next.map(n -> new CustomConstraint(anno.value(), n))
20 .orElseGet(() -> new CustomConstraint(anno.value()));
21
22 return Optional.of(processor);
23
24 }
25
26}
5.3. Bean単位での値の検証方法
CellProcessorは、フィールド1つに対する単項目チェックです。
項目間に対する相関チェックを行う場合は、CsvValidator
で1レコード分のBeanに対するチェック処理を実装します。
5.3.1. 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}
5.3.2. エラーメッセージの定義
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)}以内で入力してください。
5.3.3. エラーのハンドリング
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}
5.4. エラー処理の方法
5.4.1. 例外の種類とハンドリング
読み込み時の書式の不正や値の検証時に失敗した場合、例外 org.supercsv.exception.SuperCsvException
がスローされます。
エラー内容を画面に表示するようなシステムの場合、例外の内容をメッセージに変換する必要があります。
そのような時には、com.github.mygreen.supercsv.validation.CsvExceptionConverter
を使い、エラーメッセージに変換します。
CsvExceptionConverter は、CsvAnnotaionBeanReader/CsvAnnotaionBeanWriterに組み込まれており、 例外発生時に自動的にエラーメッセージに変換されたものがため込まれます。
java.lang.RuntimeException
└ org.supercsv.exception.SuperCsvException
├ org.supercsv.exception.SuperCsvReflectionException
├ org.supercsv.exception.SuperCsvCellProcessorException
│ ├ org.supercsv.exception.SuperCsvConstraintViolationException
│ │
│ │ ※Super Csv Annotationの例外
│ └ com.github.mygreen.supercsv.exception.SuperCsvValidationException
│
│※Super Csv Annotationの例外
├ com.github.mygreen.supercsv.exception.SuperCsvInvalidAnnotationException
├ com.github.mygreen.supercsv.exception.SuperCsvNoMatchHeaderException
├ com.github.mygreen.supercsv.exception.SuperCsvNoMatchColumnSizeException
├ com.github.mygreen.supercsv.exception.SuperCsvBindingException
└ com.github.mygreen.supercsv.exception.SuperCsvRowException
クラス名 |
説明 |
---|---|
SuperCsvException |
Super CSVのルートの例外。
Super CSV及び、Super CSV Annotationの例外は、全てこのクラスを継承しています。
|
SuperCsvReflectionException |
Beanのインスタンスなどの作成やプロパティへの値のマッピング時など、
リフレクションを使ってに失敗したときの例外。
|
SuperCsvCellProcessorException |
CellProcessor内で、処理対象のセルの値のクラスタイプが不正などのときのときにスローされる例外。
例えば、文字列を日時にパースするCellProcessor
ParseDate で、パースに失敗した場合。 |
SuperCsvConstraintViolationException |
制約のCellProcessor内で、値が制約違反となり不正のときのにスローされる例外。
例えば、CellProcessor
LMinMax で、セルの値が指定した数値の範囲以外のときにスローされる例外。 |
クラス名 |
説明 |
---|---|
SuperCsvValidationException |
Super CSV Annotationの CellProcessor
ValidationCellProcessor を実装しているCellProcessorの制約違反の例外。
メッセージ変数などの情報が格納されている。
|
SuperCsvInvalidAnnotationException |
アノテーションの値が不正だったりした場合にスローされる例外。
|
SuperCsvNoMatchHeaderException |
ヘッダー行を読み込む際に、
@CsvColumn(label="") で定義している値と異なる場合にスローされる例外。
|
SuperCsvNoMatchColumnSizeException |
ヘッダー行やレコードを読み込む際に、
@CsvColumn で定義しているカラムサイズと異なる場合にスローされる例外。
|
SuperCsvRowException |
各カラムのCellProcesor内で発生した SuperCsvCellProcessorException の例外を、
レコードを単位にまとめた例外。
|
SuperCsvBindingException |
最終的にカラムのマッピングに失敗したときにスローされる例外。
CsvValidatorによるBeanの検証時のエラーも格納されている。
|
レコードの値の読み書きを行う場合に業務例外として扱うものは、SuperCsvNoMatchColumnSizeException
と SuperCsvBindingException
の2つと考えて処理すればよいです。
他の例外は、設定が不正な場合にスローされるため、基本はシステムエラー(ランタイムエラー)として扱うことになります。
ただし、ヘッダー行の読み込み時は、SuperCsvNoMatchHeaderException
も考慮する必要があります。
CsvAnnotationBeanReader/CsvAnnotationBeanWriter は AutoCloseable
が実装されていますが、
try-with-resources 文を使用する場合は注意が必要です。アノテーションの解析などはコンストラクタ内で行うので、
もし、その中で例外が発生するとCSVファイルに関連するリソースが解放されないくなるため分割して定義します。
また、1レコードずつ処理すると、例外発生時に処理が終わってしまうため、全レコードの値を検証したい場合は、
readAll(...)/readWrite(...)
メソッドの使用をお勧めします。
例外がスローされたときの処理をtry-catchではなく、ハンドラ形式で処理したい場合は、 CsvSuccessHandler
、 CsvErrorHandler
の実装を指定します。
関数型インタフェースのため、Lambda式を使うことができます。 [v2.3+]
1import java.nio.charset.Charset;
2import java.nio.file.Files;
3import java.io.File;
4import java.io.IOException;
5import java.io.Reader;
6import java.util.ArrayList;
7import java.util.List;
8
9import org.supercsv.prefs.CsvPreference;
10import org.supercsv.exception.SuperCsvException;
11
12import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
13import com.github.mygreen.supercsv.exception.SuperCsvBindingException;
14import com.github.mygreen.supercsv.exception.SuperCsvNoMatchColumnSizeException;
15import com.github.mygreen.supercsv.exception.SuperCsvNoMatchHeaderException;
16
17
18public class Sample {
19
20 // 読み込み時の場合(1行づつ処理する場合)
21 public void sampleReadEach() {
22
23 try(Reader reader = Files.newBufferedReader(
24 new File("sample.csv").toPath(), Charset.forName("Windows-31j"));
25 CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
26 SampleCsv.class, reader, CsvPreference.STANDARD_PREFERENCE); ) {
27
28 // ヘッダー行の読み込み
29 String[] headers = csvReader.getHeader(true);
30
31 List<SampleCsv> list = new ArrayList<>();
32
33 // レコードの読み込み - 1行づつ
34 SampleCsv record = null;
35 while((record = csvReader.read()) != null) {
36 list.add(record);
37 }
38
39 } catch(SuperCsvNoMatchColumnSizeException
40 | SuperCsvBindingException
41 | SuperCsvNoMatchHeaderException e) {
42 // レコードの値が不正な場合のときのエラー
43
44 } catch(SuperCsvException e ) {
45 // Super CSVの設定などのエラー
46
47 } catch(IOException e) {
48 // ファイルI/Oに関する例外
49
50 }
51 }
52
53 // 読み込み時の場合(全件処理する場合)
54 public void sampleReadAll() {
55
56 try(Reader reader = Files.newBufferedReader(
57 new File("sample.csv").toPath(), Charset.forName("Windows-31j"));
58 CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
59 SampleCsv.class, reader, CsvPreference.STANDARD_PREFERENCE); ) {
60
61 // 全件読み込む - SuperCsvBindingExceptionなどの例外発生しても続けて処理する
62 List<SampleCsv> list = csvReader.readAll(true);
63
64 // エラーメッセージの取得
65 List<String> errorMessages = csvReader.getErrorMessages();
66
67 } catch(SuperCsvException e ) {
68 // Super CSVの設定などのエラー
69
70 } catch(IOException e) {
71 // ファイルI/Oに関する例外
72
73 }
74 }
75
76 // 読み込み時の場合(ハンドラで処理する場合)
77 public void sampleReadWithHandler() {
78
79 try(Reader reader = Files.newBufferedReader(
80 new File("sample.csv").toPath(), Charset.forName("Windows-31j"));
81 CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
82 SampleCsv.class, reader, CsvPreference.STANDARD_PREFERENCE); ) {
83
84 // ヘッダー行の読み込み
85 String[] headers = csvReader.getHeader(true);
86
87 List<SampleCsv> list = new ArrayList<>();
88
89 // ハンドラによる読み込み
90 while(csvReader.read(
91 record -> {
92 // 読み込み成功時の処理 - CsvSuccessHandler
93 list.add(record);
94 },
95 error -> {
96 // Super CSVに関するエラー処理 - CsvErrorHandler
97
98 }) != CsvReadStatus.EOF) {
99
100 }
101
102 // エラーメッセージの取得
103 List<String> errorMessages = csvReader.getErrorMessages();
104
105 } catch(IOException e) {
106 // ファイルI/Oに関する例外
107
108 }
109
110 }
111}
1import java.nio.charset.Charset;
2import java.nio.file.Files;
3import java.io.File;
4import java.io.IOException;
5import java.io.Writer;
6import java.util.ArrayList;
7import java.util.List;
8
9import org.supercsv.prefs.CsvPreference;
10import org.supercsv.exception.SuperCsvException;
11
12import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;
13import com.github.mygreen.supercsv.exception.SuperCsvBindingException;
14
15
16public class Sample {
17
18 // 書き込み時の場合(1行づつ処理する場合)
19 public void sampleWriteEach() {
20
21 try(Writer writer = Files.newBufferedWriter(
22 new File("sample.csv").toPath(), Charset.forName("Windows-31j"));
23 CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
24 SampleCsv.class, reader, CsvPreference.STANDARD_PREFERENCE); ) {
25
26 // ヘッダー行の書き込み
27 csvWriter.writeHeaader();
28
29 // レコードの書き込み - 1行づつ
30 SampleCsv record1 = /* 省略*/;
31 csvWriter.write(record1);
32
33 SampleCsv record2 = /* 省略*/;
34 csvWriter.write(record2);
35
36
37 } catch (SuperCsvBindingException e) {
38 // レコードの値が不正な場合のときのエラー
39
40 } catch(SuperCsvException e ) {
41 // Super CSVの設定などのエラー
42
43 } catch(IOException e) {
44 // ファイルI/Oに関する例外
45
46 }
47 }
48
49 // 書き込み時の場合(全件処理する場合)
50 public void sampleWriteAll() {
51
52 try(Writer writer = Files.newBufferedWriter(
53 new File("sample.csv").toPath(), Charset.forName("Windows-31j"));
54 CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
55 SampleCsv.class, writer, CsvPreference.STANDARD_PREFERENCE); ) {
56
57 List<SampleCsv> list = /* 省略 */;
58
59 // 全件書き込む - SuperCsvBindingExceptionなどの例外発生しても続けて処理する
60 csvWriter.writeAll(list, true);
61
62 // エラーメッセージの取得
63 List<String> errorMessages = csvWriter.getErrorMessages();
64
65 } catch(SuperCsvException e ) {
66 // Super CSVの設定などのエラー
67
68 } catch(IOException e) {
69 // ファイルI/Oに関する例外
70
71 }
72 }
73
74 // 書き込み時の場合(ハンドラで処理する場合)
75 public void sampleWriteWithHandler() {
76
77 try(Writer writer = Files.newBufferedWriter(
78 new File("sample.csv").toPath(), Charset.forName("Windows-31j"));
79 CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
80 SampleCsv.class, writer, CsvPreference.STANDARD_PREFERENCE); ) {
81
82 List<SampleCsv> list = /* 省略 */;
83
84 // ヘッダー行の書き込み
85 csvWriter.writeHeaader();
86
87 for(SampleCsv item : list) {
88 csvWriter.write(item, error -> {
89 // Super CSVに関するエラー処理 - CsvErrorHandler
90 }
91 );
92 }
93
94 csvWriter.flush();
95
96 // エラーメッセージの取得
97 List<String> errorMessages = csvWriter.getErrorMessages();
98
99 } catch(IOException e) {
100 // ファイルI/Oに関する例外
101
102 }
103 }
104}
5.4.2. 書き込み時の値の検証のスキップ
書き込み時の値をスキップしたい場合は、グループによる指定もできますが、システム設定を変更することで一律にスキップすることができます。
BeanMappingFactory
から Configuration
を取得し、そのプロパティ skipValidationOnWrite
の値を trueに設定します。
1import com.github.mygreen.supercsv.builder.BeanMapping;
2import com.github.mygreen.supercsv.builder.BeanMappingFactory;
3import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;
4
5import java.nio.charset.Charset;
6import java.nio.file.Files;
7import java.io.File;
8
9import org.supercsv.prefs.CsvPreference;
10
11public class Sample {
12
13 public void sampleWriteWithSkipValidation() {
14
15 // システム情報の設定変更
16 BeanMappingFactory mappingFactory = new BeanMappingFactory();
17 mappingFactory.getConfiguration().setSkipValidationOnWrite(true);
18
19 // BeanMappingの作成
20 BeanMapping<SampleCsv> beanMapping = mappingFactory.create(SampleCsv.class);
21
22 CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
23 beanMapping,
24 Files.newBufferedWriter(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
25 CsvPreference.STANDARD_PREFERENCE);
26
27 //... 以下省略
28 }
29
30}
5.5. 値の検証時のエラーメッセージ
セルの値の検証時など例外がスローされ、 CsvExceptionConverter
によりメッセージに変換します。
CsvExceptionConverter は、CsvAnnotationBeanReader/CsvAnnotationBeanWriter に設定します。
メッセージリソースは MessageResolver
で管理されてます。
デフォルトでは、
ResourceBundleMessageResolver
が設定されています。ResourceBundleMessageResolver では、システムのプロパティファイル
com/github/mygreen/supercsv/localization/SuperCsvMessages.properties
が読み込まれます。独自のメッセージは、クラスパスのルート配置した
SuperCsvMessages.properties
が読み込まれます。
実装を切り替えることで、他の形式のファイルからも取得することができます。
クラス名 |
説明 |
---|---|
ResourceBundleMessageResolver |
java.util.ResourceBundle を経由してメッセージを参照します。デフォルトでは
SuperCsvMessages.properties を読み込みます。 |
PropertiesMessageResolver |
java.util.Properties を経由してメッセージを参照します。 |
SpringMessageResolver |
org.springframework.context.MessageSource を経由してメッセージを参照します。詳細は、 Spring Frameworkとの連携(エラーメッセージの設定方法) を参照してください。
|
また、メッセージ中には {var}
の形式で変数が可能です。
さらに、${exp}
の形式で 式言語の Java Expression Language (JEXL) が利用可能です。
デフォルト設定では、式言語ので呼び出し可能な関数が登録されています。
com.github.mygreen.supercsv.expression.CustomFunction
のメソッドが接頭語 f: を付けて呼び出し可能です。
また、独自の関数も登録可能です。
1import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
2import com.github.mygreen.supercsv.localization.MessageInterpolator;
3import com.github.mygreen.supercsv.localization.ResourceBundleMessageResolver;
4import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
5import com.github.mygreen.supercsv.expression.ExpressionLanguageJEXLImpl;
6
7import java.nio.charset.Charset;
8import java.nio.file.Files;
9import java.io.File;
10import java.util.ResourceBundle;
11
12import org.supercsv.prefs.CsvPreference;
13
14public class Sample
15 {
16
17 public void customMessageAndExpression() {
18
19 // CsvExceptionConverterの作成
20 CsvExceptionConverter exceptionConverter = new CsvExceptionConverter();
21
22 // メッセージソースを既存の物に対して追加する
23 ResourceBundleMessageResolver messageResolver = new ResourceBundleMessageResolver();
24 messageResolver.addResourceBundle(ResourceBundle.getBundle("SampleMessages"));
25 exceptionConverter.setMessageResolver(messageResolver);
26
27 // 式言語に独自の関数を登録
28 // 参照可能な関数は、public static である必要があります。
29 ExpressionLanguageJEXLImpl el = new ExpressionLanguageJEXLImpl();
30 Map<String, Object> funcs = new HashMap<>();
31 funcs.put("my", SampleFunctions.class);
32 el.getJexlEngine().setFunctions(funcs);
33
34 // カスタマイズした式言語の登録
35 exceptionConverter.setMessageInterpolator(new MessageInterpolator(el));
36
37 // CsvExceptionConverterを設定する
38 CsvAnnotationBeanReader<UserCsv> csvReader = new CsvAnnotationBeanReader<>(
39 UserCsv.class,
40 Files.newBufferedReader(new File("user.csv").toPath(), Charset.forName("Windows-31j")),
41 CsvPreference.STANDARD_PREFERENCE);
42
43 csvReader.setExceptionConverter(exceptionConverter);
44
45 //... 以下省略
46 }
47
48}
5.6. Bean Validationとの連携
Bean Validationによるカラムの値の検証を行う方法を説明します。
5.6.1. ライブラリの追加
Bean Validationを利用する際には、ライブリを追加します。 Mavenを利用している場合は、pom.xmlに以下を追加します。
本ライブラリは、Bean Validation1.0/1.1の両方に対応しており、 その参照実装である Hibernate Validator を追加します。
Bean Validation 1.1(JSR-349)を利用する場合は、Hibernate Validator5.x系を利用します。 さらに、メッセージ中にJava EEのEL式が利用可能となっているため、その実装であるライブリを追加します。
ただし、TomcatやGlassFishなどのWebコンテナ上で使用するのであれば、EL式のライブラリはそれらに組み込まれているため必要ありません。
また、本ライブラリの機能を利用して、JEXL に切り替えるのであれば、式言語の追加は必要ありません。
1<!-- Bean Validation 1.1 -->
2<dependency>
3 <groupId>javax.validation</groupId>
4 <artifactId>validation-api</artifactId>
5 <version>1.1.0.Final</version>
6</dependency>
7<dependency>
8<groupId>org.hibernate</groupId>
9 <artifactId>hibernate-validator</artifactId>
10 <version>5.3.3.Final</version>
11</dependency>
12
13<!-- EL式のライブラリが必要であれば追加します -->
14<dependency>
15 <groupId>org.glassfish</groupId>
16 <artifactId>javax.el</artifactId>
17 <version>3.0.1-b08</version>
18</dependency>
Bean Validation 1.0(JSR-303)を利用する場合は、Hibernate Validator4.x系を利用します。 Bean Validation 1.0では、メッセージ中でEL式は利用できませんが、本ライブラリの機能を使用すれば、JEXL が利用できます。
1<!-- Bean Validation 1.0 -->
2<dependency>
3<groupId>javax.validation</groupId>
4 <artifactId>validation-api</artifactId>
5 <version>1.0.0.GA</version>
6</dependency>
7<dependency>
8 <groupId>org.hibernate</groupId>
9 <artifactId>hibernate-validator</artifactId>
10 <version>4.3.2.Final</version>
11</dependency>
5.6.2. Bean Validationの利用方法
アノテーション @CsvBean(validatosr=CsvBeanValidator.class)
を指定します。
CsvBeanValidator
は、Bean Validation と、本ライブラリの CsvValidator
をブリッジするクラスです。
独自のメッセージソースは、クラスパスのルートに HibernateValidation.properties
を配置しておけば自動的に読み込まれます。
1import com.github.mygreen.supercsv.annotation.CsvBean;
2import com.github.mygreen.supercsv.annotation.CsvColumn;
3import com.github.mygreen.supercsv.validation.beanvalidation.CsvBeanValidator;
4
5import javax.validation.constraints.AssertTrue;
6import javax.validation.constraints.DecimalMax;
7import javax.validation.constraints.Pattern;
8
9import org.hibernate.validator.constraints.Length;
10import org.hibernate.validator.constraints.NotEmpty;
11import org.hibernate.validator.constraints.Range;
12
13// Bean Validationの指定方法
14@CsvBean(validators=CsvBeanValidator.class)
15private static class TestCsv {
16
17 @CsvColumn(number=1)
18 @NotEmpty
19 private String id;
20
21 @CsvColumn(number=2)
22 @Length(max=10)
23 @Pattern(regexp="[\\p{Alnum}]+", message="半角英数字で設定してください。")
24 private String name;
25
26 @CsvColumn(number=3)
27 @Range(min=0, max=100)
28 private Integer age;
29
30 @CsvColumn(number=4)
31 boolean used;
32
33 // 相関チェック
34 @AssertTrue(message="{name}が設定されている際には、{age}は必須です。")
35 boolean isValidAgeRequired() {
36 if(name != null && !name.isEmpty()) {
37 return age != null;
38 }
39
40 return false;
41 }
42
43 // setter/gettterは省略
44}
Bean Validation による値の検証でエラーがある場合は、例外 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}
5.6.3. Bean Validationのカスタマイズ
本ライブラリ用のメッセージソースや JEXL に切り替える場合、 Bean Validationのインスタンスを変更する必要があります。
その場合は、@CsvBean(validators=CsvBeanValidator.class)
で指定するのではなく、
メソッド CsvAnnotationBeanReader#addValidators(...)
、CsvAnnotationBeanWriter#addValidators(...)
で直接追加します。
1import com.github.mygreen.supercsv.builder.BeanMapping;
2import com.github.mygreen.supercsv.builder.BeanMappingFactory;
3import com.github.mygreen.supercsv.localization.MessageInterpolator;
4import com.github.mygreen.supercsv.localization.MessageResolver;
5import com.github.mygreen.supercsv.localization.ResourceBundleMessageResolver;
6
7import java.nio.charset.Charset;
8import java.nio.file.Files;
9import java.io.File;
10
11import org.supercsv.prefs.CsvPreference;
12
13public class Sample {
14
15 // Beanの定義(validatorsの指定は行わない)
16 @CsvBean
17 public static class SampleCsv {
18 // 省略
19 }
20
21 public void sampleBeanValidationCustom() {
22
23 // CsvReaderの作成
24 CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
25 SampleCsv.class,
26 Files.newBufferedReader(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
27 CsvPreference.STANDARD_PREFERENCE);
28
29 // BeanValidator用のValidatorの作成
30 final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
31
32 // メッセージ処理クラスを本ライブラリのものに入れ替えてインスタンスを生成する
33 final Validator beanValidator = validatorFactory.usingContext()
34 .messageInterpolator(new MessageInterpolatorAdapter(
35 new ResourceBundleMessageResolver(),
36 new MessageInterpolator()))
37 .getValidator();
38
39 // Validatorの追加
40 csvReader.addValidators(beanValidator);
41
42 }
43
44}
メッセージ中の変数として、既存の変数に加えて、CSV用の次の変数が登録されており利用可能です。
変数名 |
説明 |
---|---|
lineNumber |
CSVの実ファイル上の行番号。
カラムの値に改行が含まれている場合を考慮した実際の行番号なります。
1から始まります。
|
rowNumber |
CSVの論理上の行番号です。
1から始まります。
|
columnNumber |
CSVの列番号です。
1から始まります。
|
label |
@CsvColumn(label="<見出し>") 出指定したカラムの見出し名です。label属性を指定していない場合は、フィールド名になります。
|
validatedValue |
不正となった値です。
|
printer |
各クラスの
TextFormatter のインスタンスです。${printer.print(validatedValue)} で、オブジェクトをフォーマットするのに利用します。 |
5.6.4. BeanValidationとSpring Frameworkとの連携
Spring Frameworkと連携することで、コードがシンプルになります。 また、独自のフィールド用のValidator内にSpringBeanをインジェクションすることも可能です。
5.6.4.1. XMLによるコンテナの設定
XMLによる設定方法を説明します。
コンテナの定義の基本は次のようになります。
アノテーションによるDIの有効化を行います。
コンポーネントスキャン対象のパッケージの指定を行います。
com.github.mygreen.supercsv.builder.SpringBeanFactory
をSpringBeanとして登録します。Springの
MessageSource
で、本ライブラリのエラーメッセージcom.github.mygreen.supercsv.localization.SuperCsvMessages
を読み込んでおきます。 * 独自のエラーメッセージがあれば、追加で定義します。com.github.mygreen.supercsv.localization.SpringMessageResolver
に、MessageSource
を渡します。
CsvBeanValidator
に、SpringのLocalValidatorFactoryBean
で作成したBeanValidationのValidtorのインスタンスを渡します。
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- XMLによるコンテナの定義 -->
3<beans xmlns="http://www.springframework.org/schema/beans"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:context="http://www.springframework.org/schema/context"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8 http://www.springframework.org/schema/context
9 http://www.springframework.org/schema/context/spring-context.xsd
10 ">
11
12 <!-- アノテーションによるDIの有効化の定義 -->
13 <context:annotation-config />
14
15 <!-- コンポーネントスキャン対象のパッケージの指定 -->
16 <context:component-scan base-package="sample.spring" />
17
18 <!-- Springのコンテナを経由するCSV用のBeanFactoryの定義 -->
19 <bean id="springBeanFacatory" class="com.github.mygreen.supercsv.builder.SpringBeanFactory" />
20
21 <!-- Spring標準のメッセージソースの定義 -->
22 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
23 <property name="basenames">
24 <list>
25 <value>com.github.mygreen.supercsv.localization.SuperCsvMessages</value>
26 <value>TestMessages</value>
27 </list>
28 </property>
29 </bean>
30
31 <!-- Super CSV Annotation 用のMessgeResolverの定義 -->
32 <bean id="springMessageResolver" class="com.github.mygreen.supercsv.localization.SpringMessageResolver">
33 <property name="messageSource" ref="messageSource" />
34 </bean>
35
36 <!-- BeanValidation用のCsvValidatorの定義 -->
37 <bean id="csvBeanValidator" class="com.github.mygreen.supercsv.validation.beanvalidation.CsvBeanValidator">
38 <constructor-arg>
39 <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
40 <property name="messageInterpolator">
41 <bean class="com.github.mygreen.supercsv.validation.beanvalidation.MessageInterpolatorAdapter">
42 <constructor-arg ref="springMessageResolver" />
43 <constructor-arg><bean class="com.github.mygreen.supercsv.localization.MessageInterpolator" /></constructor-arg>
44 </bean>
45 </property>
46 </bean>
47 </constructor-arg>
48 </bean>
49
50</beans>
5.6.4.2. JavaConfigによるコンテナの設定
Spring Framework3.0から追加された、JavaソースによるSpringBean定義の方法を説明します。
JavaConfigによる設定を使用する場合は、Spring Frameworkのバージョンをできるだけ最新のものを使用してください。 特に、機能が豊富なバージョン4.0以上の使用を推奨します。
1import javax.validation.Validator;
2
3import org.springframework.context.MessageSource;
4import org.springframework.context.annotation.Bean;
5import org.springframework.context.annotation.ComponentScan;
6import org.springframework.context.annotation.Configuration;
7import org.springframework.context.annotation.Description;
8import org.springframework.context.support.ResourceBundleMessageSource;
9import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
10
11import com.github.mygreen.supercsv.builder.SpringBeanFactory;
12import com.github.mygreen.supercsv.localization.MessageInterpolator;
13import com.github.mygreen.supercsv.localization.SpringMessageResolver;
14import com.github.mygreen.supercsv.validation.beanvalidation.CsvBeanValidator;
15import com.github.mygreen.supercsv.validation.beanvalidation.MessageInterpolatorAdapter;
16
17
18// Javaによるコンテナの定義
19@Configuration
20@ComponentScan(basePackages="sample.spring")
21public class SuperCsvConfig {
22
23 @Bean
24 @Description("Springのコンテナを経由するCSV用のBeanFactoryの定義")
25 public SpringBeanFactory springBeanFactory() {
26 return new SpringBeanFactory();
27 }
28
29 @Bean
30 @Description("Spring標準のメッセージソースの定義")
31 public MessageSource messageSource() {
32 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
33 messageSource.addBasenames("com.github.mygreen.supercsv.localization.SuperCsvMessages", "TestMessages");
34 return messageSource;
35 }
36
37 @Bean
38 @Description("本ライブラリのSpring用のMessgeResolverの定義")
39 public SpringMessageResolver springMessageResolver() {
40 return new SpringMessageResolver(messageSource());
41 }
42
43 @Bean
44 @Description("Spring用のBeanValidatorのValidatorの定義")
45 public Validator csvLocalValidatorFactoryBean() {
46
47 LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
48
49 // メッセージなどをカスタマイズ
50 validator.setMessageInterpolator(new MessageInterpolatorAdapter(
51 springMessageResolver(), new MessageInterpolator()));
52 return validator;
53 }
54
55 @Bean
56 @Description("CSV用のCsvValidaotrの定義")
57 public CsvBeanValidator csvBeanValidator() {
58
59 // ValidarorのインスタンスをSpring経由で作成したものを利用する
60 CsvBeanValidator csvBeanValidator = new CsvBeanValidator(csvLocalValidatorFactoryBean());
61 return csvBeanValidator;
62 }
63
64}
5.6.4.3. 独自のConstraintValidatorの作成
Bean Validationの独自のアノテーションを作成する際には、通常の方法と同じです。
メタアノテーション
@Constraint
を付与します。属性
validatedBy
に、ConsraintValidator
の実装クラスを指定します。
複数指定可能できるように、内部クラス
List
を定義しておきます。Bean Validation 1.1の段階では、Java8から追加された
@Repeatable
は対応していませんが、 従来の定義方法と揃えておくことで、@Repeatable を使ってJava8のスタイルで使用することができます。ただし、今後リリース予定のBeanValidator2.0から @Repeatable 対応するため、定義しておいても問題はありません。
1import java.lang.annotation.Documented;
2import java.lang.annotation.ElementType;
3import java.lang.annotation.Repeatable;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8import javax.validation.Constraint;
9import javax.validation.Payload;
10
11//BeanValidationの制約のアノテーション
12@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
13@Retention(RetentionPolicy.RUNTIME)
14@Documented
15@Repeatable(UserMailPattern.List.class) // 対応していないので、定義しなくても良い。
16@Constraint(validatedBy=UserMailPatternValidator.class)
17public @interface UserMailPattern {
18
19 // 共通の属性の定義
20 Class<?>[] groups() default {};
21 String message() default "{sample.spring.UserMailPattern.message}";
22 Class<? extends Payload>[] payload() default {};
23
24 // 複数のアノテーションを指定する場合の定義
25 @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
26 @Retention(RetentionPolicy.RUNTIME)
27 @Documented
28 @interface List {
29 UserMailPattern[] value();
30 }
31
32}
ConstraintValidator
の実装中で、SpringBeanのインジェクションを行いたい場合は、アノテーション @Resource/@Autowired
など使います。
ConstraintValidator 自身は、SpringBeanとして登録する必要はありません。
1import javax.validation.ConstraintValidator;
2import javax.validation.ConstraintValidatorContext;
3
4import org.springframework.beans.factory.annotation.Autowired;
5
6
7// ConstraintValidatorの実装
8public class UserMailPatternValidator implements ConstraintValidator<UserMailPattern, String> {
9
10 // SpringBeanをインジェクションします。
11 @Autowired
12 private UserService userService;
13
14 @Override
15 public void initialize(final UserMailPattern constraintAnnotation) {
16
17 }
18
19 @Override
20 public boolean isValid(final String value, final ConstraintValidatorContext context) {
21
22 // nullの場合は対象外
23 if(value == null) {
24 return true;
25 }
26
27 return userService.isMailPattern(value);
28 }
29
30}
5.6.4.4. CsvBeanの定義
CSVのBeanの定義では、@CsvBean(validator=CsvBeanValidator.class)
で、CsvBeanValidatorを指定します。
1import com.github.mygreen.supercsv.annotation.CsvBean;
2import com.github.mygreen.supercsv.annotation.CsvColumn;
3import com.github.mygreen.supercsv.validation.beanvalidation.CsvBeanValidator;
4
5
6@CsvBean(header=true, validator=CsvBeanValidator.class)
7public class UserCsv {
8
9 @CsvColumn(number=1, label="メールアドレス")
10 @UserMailPattern // 独自のBeanValidator用のアノテーションの指定
11 private String mail;
12
13 // setter/getterは省略
14
15}
5.6.4.5. 値の検証方法
BeanMappingFactory#getConfiguration()
取得できる、システム設定に、SpringBeanFactoryを設定します。CsvExceptionConverter#setMessageResolver(...)
に、SpringBeanとして定義したSpringMessageResolver
を設定します。さらに、
CsvAnnotationBeanReader#setExceptionConverter(...)
に、作成した CsvExceptionConverter を渡します。
1import com.github.mygreen.supercsv.builder.BeanMapping;
2import com.github.mygreen.supercsv.builder.BeanMappingFactory;
3import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
4
5import java.nio.charset.Charset;
6import java.nio.file.Files;
7import java.io.File;
8import java.util.ArrayList;
9import java.util.List;
10
11import org.supercsv.prefs.CsvPreference;
12import org.supercsv.exception.SuperCsvException;
13
14@Service
15public class CsvService {
16
17 @Autowired
18 private SpringBeanFactory beanFactory;
19
20 @Autowired
21 private SpringMessageResolver messageResolver;
22
23 public void sampleSpring() {
24
25 // BeanMappingの作成 - SpringBeanFactoryを設定する
26 BeanMappingFactory beanMappingFactory = new BeanMappingFactory();
27 beanMappingFactory.getConfiguration().setBeanFactory(beanFactory);
28
29 // BeanMappingの作成
30 BeanMapping<UserCsv> beanMapping = mappingFactory.create(UserCsv.class);
31
32 CsvAnnotationBeanReader<UserCsv> csvReader;
33 try {
34 csvReader = new CsvAnnotationBeanReader<>(
35 beanMapping,
36 Files.newBufferedReader(new File("user.csv").toPath(), Charset.forName("Windows-31j")),
37 CsvPreference.STANDARD_PREFERENCE);
38
39 // CsvExceptionConverterの作成 - SpringMessageResolverを設定する
40 CsvExceptionConverter exceptionConverter = new CsvExceptionConverter();
41 exceptionConverter.setMessageResolver(messageResolver);
42
43 // CsvExceptionConverterを設定する
44 svReader.setExceptionConverter(exceptionConverter);
45
46 // ファイルの読み込み
47 List<SampleCsv> list = csvReader.readAll();
48
49 } catch(SuperCsvException e) {
50
51 // 変換されたエラーメッセージの取得
52 List<String> messages = csvReader.getErrorMessages();
53
54 } finally {
55 if(csvReader != null) {
56 csvReader.close();
57 }
58 }
59 }
60
61}