4. 値の変換方法

読み込み時の文字列を各クラスにタイプにパースする前と、書き込み時の各クラスから文字列にフォーマットした後の文字列に対して、 値を変換することができます。

例えば、トリミングや大文字に変換などを行うことができます。

4.1. 変換処理用の既存のアノテーション

既存のアノテーションとして、以下のものが用意されています。

表 - 4.1.1 変換規則を指定する既存のアノテーション

アノテーション

概要

参照

@CsvTrim

前後の空白をトリミングします。

JavaDoc

@CsvOneSideTrim [v2.1+]

前後どちらか一方をトリミングします。

JavaDoc

@CsvDefaultValue

値がnullのときに他の値に変換します。

JavaDoc

@CsvNullConvert

指定した値と一致するときにnullに変換します。

JavaDoc

@CsvLower

英字のアルファベットの大文字から小文字に変換します。

JavaDoc

@CsvUpper

英字のアルファベットの小文字を大文字に変換します。

JavaDoc

@CsvRegexReplace

正規表現による置換を行います。

JavaDoc

@CsvWordReplace

語彙による置換を行います。

JavaDoc

@CsvFullChar

半角文字を日本語の全角文字に変換します。

JavaDoc

@CsvHalfChar

日本語の全角文字を半角文字に変換します。

JavaDoc

@CsvTruncate

一定の文字長を超える場合に切り出しを行います。

JavaDoc

@CsvLeftPad

左側にパディングを行います。

JavaDoc

@CsvRightPad

右側にパディングを行います。

JavaDoc

@CsvMultiPad [v2.1+]

柔軟な設定でパディングを行います。

JavaDoc

@CsvFixedSize [v2.1+]

固定長のサイズに変換します。
詳細は、「 固定長のカラムの読み書き 」を参照してください。

JavaDoc

4.1.1. 処理順序の指定

属性 order で処理順序を指定することができます。

  • 値が大きいほど後から実行されます。

  • 値が同じ場合は、アノテーションのFQCN(完全限定クラス名)の昇順で実行されます。

    • 属性orderを省略した場合は、デフォルト値 0 が適用されます。

  • 読み込み時、書き込み時とも同じ処理順序になります。

  • 属性 order が付与されていないアノテーションは順番が付与されているものよりも後になります。

 1import com.github.mygreen.supercsv.annotation.CsvBean;
 2import com.github.mygreen.supercsv.annotation.CsvColumn;
 3
 4import com.github.mygreen.supercsv.annotation.conversion.*;
 5
 6@CsvBean
 7public class SampleCsv {
 8
 9    // 空白の場合、トリミングして空文字となった場合に入力値なしと判断して、nullに変換します。
10    @CsvColumn(number=1)
11    @CsvTrim(order=1)
12    @CsvNullConvert(value="", order=2)
13    private String comment;
14
15    // getter/setterは省略
16}

4.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.conversion.*;
 4import com.github.mygreen.supercsv.builder.BuildCase;
 5
 6@CsvBean
 7public class SampleCsv {
 8
 9    // 空白の場合、トリミングして空文字となった場合に入力値なしと判断して、nullに変換します。
10    @CsvColumn(number=1)
11    @CsvTrim(order=1, cases={})  // 全てのケースに適用
12    @CsvNullConvert(value="N/A", cases=BuildCase.Read)  // 読み込み時のみ適用
13    @CsvDefault(value="N/A", cases=BuildCase.Write)     // 書き込み時のみ適用
14    private String comment;
15
16    // getter/setterは省略
17}

4.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;
 4
 5import com.github.mygreen.supercsv.annotation.conversion.*;
 6
 7@CsvBean
 8public class SampleCsv {
 9
10    @CsvColumn(number=1)
11    @CsvHalfChar(order=1)
12    @DefaultValue(value="10", groups=AdminGroup.class, order=2)
13    @DefaultValue(value="0", groups=NormalGroup.class, order=2)
14    private Integer value;
15
16    // getter/setterは省略
17}
18
19// グループ用クラスの作成
20public static interface AdminGroup {}
21public 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
14
15public class Sample {
16
17    // 読み込み時のグループの指定
18    public void sampleRead() {
19
20        CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
21                SampleCsv.class,
22                Files.newBufferedReader(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
23                CsvPreference.STANDARD_PREFERENCE,
24                DefaultGroup.class, AdminGroup.class); // デフォルトとAdminのグループクラスを指定する。
25
26        //... 以下省略
27
28    }
29
30    // 書き込み時のグループの指定
31    public void sampleWrite() {
32
33        CsvAnnotationBeanWriter<SampleCsv> csvWriter = new CsvAnnotationBeanWriter<>(
34                SampleCsv.class,
35                Files.newBufferedWriter(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
36                CsvPreference.STANDARD_PREFERENCE,
37                DefaultGroup.class, NormalGroup.class); // デフォルトとNoraml用のグループクラスを指定する。
38
39        //... 以下省略
40
41    }
42
43    // BeanMapping作成時の指定
44    public void sampleBeanMapping() {
45
46        // BeanMappingの作成
47        BeanMappingFactory mappingFactory = new BeanMappingFactory();
48        BeanMapping<SampleCsv> beanMapping = mappingFactory.create(SampleCsv.class,
49            DefaultGroup.class, NormalGroup.class);  // デフォルトとNoraml用のグループクラスを指定する。
50
51        CsvAnnotationBeanReader<SampleCsv> csvReader = new CsvAnnotationBeanReader<>(
52                beanMapping,
53                Files.newBufferedReader(new File("sample.csv").toPath(), Charset.forName("Windows-31j")),
54                CsvPreference.STANDARD_PREFERENCE);
55
56        //... 以下省略
57    }
58
59}

4.2. 独自の変換処理の作成方法

独自の変換処理を実装するには、3つのステップを踏む必要があります。

  1. CellProcessorの実装クラスの作成

  2. アノテーションクラスの作成

  3. CellProcessorを作成するためのファクトリクラスの作成

以下、それぞれに対して解説していきます。

4.2.1. CellProcessorの実装クラスの作成

サンプルとして、任意の文字列を追加するCellProcessorを作成します。

  • 抽象クラス CellProcessorAdaptor [ JavaDoc ] を継承します。

  • CellProcessorは、「Chain of Responsibility」パターンであるため、その構造を表現するためのクラスとなります。

  • インタフェースとして StringCellProcessor [ JavaDoc ] を実装します。

    • この実装は特に必要ないですが、扱うカラムの値の種類を表現するめのものです。 CellProcessorを直接組み立てる時に、これらのインタフェースでchainとして追加する次のCellProcessorを限定するために使用します。

    • 変換処理は、必ず文字列に対して行うため実装しておきます。

  • コンストラクタとして、chainの次の処理となるCellProcessorを引数に持つものと、持たないものを必ず2つ実装します。

  • メソッド execute(...) 内で処理の実装を行います。

    • nullの場合、次の処理に委譲するようにします。 Super CSVの既存のCellProcessorではメソッドvalidateInputNotNull(...)を呼びnullチェックを行いますが、 本ライブラリではnullに対する処理は他のCellProcessorで行うため、次の処理に渡します。

    • 変換した値を次の処理に渡します。

 1import org.supercsv.cellprocessor.CellProcessorAdaptor;
 2import org.supercsv.cellprocessor.ift.CellProcessor;
 3import org.supercsv.cellprocessor.ift.StringCellProcessor;
 4import org.supercsv.util.CsvContext;
 5
 6// 独自の変換用のCellProcessor
 7public class CustomConversion extends CellProcessorAdaptor implements StringCellProcessor {
 8
 9    private String text;
10
11    public CustomConversion(final String text) {
12        super();
13        checkPreconditions(text);
14        this.text = text;
15    }
16
17    public CustomConversion(final String text, final CellProcessor next) {
18        super(next);
19        checkPreconditions(text);
20        this.text = text;
21    }
22
23    // コンストラクタで渡した独自の引数のチェック処理
24    private static void checkPreconditions(final String text) {
25        if(text == null) {
26            throw new NullPointerException("text should not be null.");
27        }
28    }
29
30    @Override
31    public <T> T execute(final Object value, final CsvContext context) {
32        if(value == null) {
33            // nullの場合、次の処理に委譲します。
34            return next.execute(value, context);
35        }
36
37        // 最後尾に文字列を足す
38        final String result = value.toString() + text;
39
40        // 変換した値を次の処理に委譲します。
41        return next.execute(result, context);
42    }
43
44}

4.2.2. 変換処理用のアノテーションクラスの作成

  • @Target として、ElementType.FIELDElementType.ANNOTATION_TYPE の2つを指定します。

    • 通常はFieldのみで問題ないですが、 アノテーションを合成 するときがあるため、 ANNOTATION_TYPE も追加しておきます。

  • @Repeatable として、複数のアノテーションを設定できるようにします。

    • 内部クラスのアノテーションとして、 List を定義します。

  • 変換用のアノテーションと示すためのメタアノテーション @CsvConversion [ JavaDoc ]を指定します。

  • 共通の属性として、 casesgroupsorder を定義します。

  • 固有の属性 として、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.conversion.CsvConversion;
 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(CsvCustomConversion.List.class)
17@CsvConversion(CustomConversionFactory.class)  // ファクトリクラスを指定
18public @interface CsvCustomConversion {
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        CsvCustomConversion[] value();
39    }
40}

4.2.3. 変換処理用のファクトリクラスの作成

アノテーションをハンドリングして、CellProcessorを作成するためのファクトリクラスを作成します。

  • インタフェース ConversionProcessorFactory [ JavaDoc ] を実装します。

  • 独自のCellProcessorのCustomConversionのインスタンスを作成します。

  • 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.ConversionProcessorFactory;
 5import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
 6
 7public class CustomConversionFactory implements ConversionProcessorFactory<CsvCustomConversion> {
 8
 9    @Override
10    public Optional<CellProcessor> create(CsvCustomConversion anno, Optional<CellProcessor> next,
11            FieldAccessor field, TextFormatter<?> formatter, Configuration config) {
12
13        // CellProcessorのインスタンスを作成します
14        final CustomConversion processor = next.map(n ->  new CustomConversion(anno.text(), n))
15                .orElseGet(() -> new CustomConversion(anno.text()));
16
17        return Optional.of(processor);
18
19    }
20
21}