3. 表・セルのマッピング方法

表やセルをマッピングするためには様々なアノテーションが用意されています。

様々な表に対応するため、マッピング方法を指定するためのアノテーションと補助的に詳細なオプションを指定するためのアノテーションがあります。

もし、独自の構造の表をマッピングするならば、「 独自の表・セルのマッピング方法 」を参照してください。

表 - 3.1 基本的なマッピング用のアノテーション

アノテーション

概要

@XlsSheet

シートをマッピングするためにクラスに付与し使用します。

@XlsSheetName

シート名をマッピングします。

@XlsCell

セルの座標を直接指定してマッピングします。

@XlsLabelledCell

見出し付きのセルをマッピングします。

@XlsArrayCells

連続し隣接するセルを配列またはリストにマッピングします。

@XlsLabelledArrayCells

見出し付きの連続し隣接するセルを配列またはリストにマッピングします。

@XlsHorizontalRecords

水平方向に連続する行のレコードを持つ表をマッピングします。

@XlsVerticalRecords

垂直方向に連続する列のレコードを持つ表をマッピングします。

@XlsColumn

レコードのカラムをマッピングします。

@XlsMapColumns

レコードの可変長のカラムをマッピングします。

@XlsArrayColumns

レコードの隣接するカラムをマッピングします。

@XlsNestedRecords

入れ子構造のレコードをマッピングします。

@XlsIterateTables

シート内で繰り返される同一構造の表をマッピングします。

@XlsComment

セルの座標を直接指定して、セルのコメントをマッピングします。

@XlsLablledComment

指定したラベルセルのコメント情報をマッピングします。

表 - 3.2 補助的なマッピング用のアノテーション

アノテーション

概要

@XlsOrder

フィールドの処理順序を指定するために使用します。

@XlsIgnorable

レコードを読み飛ばすことが可能か判定するためのメソッドに使用します。

@XlsArrayOption

連続し隣接するセルを配列またはリストを書き込む際の制御を行います。

@XlsRecordOption

レコードを書き込む際の制御を行います。

@XlsRecordFinder

レコードの開始位置をプログラマティックに指定します。

@XlsCommentOption

書き込み時のセルのコメントのサイズなどの制御を指定します。

3.1. @XlsSheet

マッピング対象のシートを「シート番号」「シート名」「シート名に対する正規表現」のいずれかで指定します。

クラスに付与します。

コード - 3.1.1 シート番号で指定する場合
1
2
3
4
@XlsSheet(number=0)
public class SampleSheet {
    ...
}
コード - 3.1.2 シート名で指定する場合
1
2
3
4
@XlsSheet(name="Users")
public class SampleSheet {
    ...
}
コード - 3.1.3 シート名を正規表現で指定する場合
1
2
3
4
@XlsSheet(regex="Users.+")
public class SampleSheet {
    ...
}

3.1.1. シート名を正規表現で指定する場合

正規表現で指定する場合は、 XlsMapper#loadMultiple(...) メソッドを用いることで、同じ形式の一致した複数シートの情報を一度に取得することができます。

書き込み時は、複数のシートが一致する可能性があり、1つに特定できない場合があるため注意が必要です。

  • 正規表現に一致するシートが1つしかない場合は、そのまま書き込みます。[ver0.5+]

  • 正規表現に一致するシートが複数ある場合、アノテーション @XlsSheetName を付与したフィールドの値を元に決定します。 そのため、予めフィールドに設定しておく必要があります。

  • アノテーション @XlsSheetName を付与しているフィールドを指定し、その値に一致しなくても、正規表現に一致するシートが1つ一致すれば、そのシートに書き込まれます。[ver0.5+]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@XlsSheet(regex="Sheet_[0-9]+")
public class SampleSheet {

    // シート名をマッピングするフィールド
    @XlsSheetName
    private String sheetName;
    ...
}


// 正規表現による複数のシートを出力する場合。
// 書き込み時に、シート名を設定して、一意に関連づけます。
SampleSheet sheet1 = new SampleSheet();
sheet1.sheetName = "Sheet_1"; // シート名の設定

SampleSheet sheet2 = new SampleSheet();
sheet2.sheetName = "Sheet_2"; // シート名の設定

SampleSheet sheet3 = new SampleSheet();
sheet3.sheetName = "Sheet_3"; // シート名の設定

// 複数のシートの書き込み
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.saveMultiple(new FileInputStream("template.xls"),
    new FileOutputStream("out.xls"),
    new Object[]{sheet1, sheet2, sheet3}
);

動的に書き込むシート数が変わるような場合は、下記のように、テンプレート用のシートをコピーしてから処理を行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 正規表現による複数のシートを出力する場合。
// 書き込み時に、シート名を設定して、一意に関連づけます。
SampleSheet sheet1 = new SampleSheet();
sheet1.sheetName = "Sheet_1"; // シート名の設定

SampleSheet sheet2 = new SampleSheet();
sheet2.sheetName = "Sheet_2"; // シート名の設定

SampleSheet sheet3 = new SampleSheet();
sheet3.sheetName = "Sheet_3"; // シート名の設定

SampleSheet[] sheets = new SampleSheet[]{sheet1, sheet2, sheet3};

// シートのクローン
Workbook workbook = WorkbookFactory.create(new FileInputStream("template.xlsx"));
Sheet templateSheet = workbook.getSheet("XlsSheet(regexp)");
for(SampleSheet sheetObj : sheets) {
    int sheetIndex = workbook.getSheetIndex(templateSheet);
    Sheet cloneSheet = workbook.cloneSheet(sheetIndex);
    workbook.setSheetName(workbook.getSheetIndex(cloneSheet), sheetObj.sheetName);
}

// コピー元のシートを削除する
workbook.removeSheetAt(workbook.getSheetIndex(templateSheet));

// クローンしたシートファイルを、一時ファイルに一旦出力する。
File cloneTemplateFile = File.createTempFile("template", ".xlsx");
workbook.write(new FileOutputStream(cloneTemplateFile));

// 複数のシートの書き込み
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.saveMultiple(
        new FileInputStream(cloneTemplateFile), // クローンしたシートを持つファイルを指定する
        new FileOutputStream("out.xlsx"),
        sheets);

3.2. @XlsSheetName

シート名をString型のプロパティにマッピングします。

コード - 3.2.1 基本的な使い方
1
2
3
4
5
6
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsSheetName
    private String sheetName;
}

注釈

書き込み時に、アノテーション @XlsSheet(regex="<シート名>") にて、 シート名を正規表現で指定している場合は、 @XlsSheetName を付与しているフィールドで書き込むシートを決定します。

そのため書き込む前に、シート名を指定する必要があります。

3.2.1. メソッドにアノテーションを付与する場合

アノテーションをメソッドに付与する場合、書き込み時はgetterメソッドメソッドの付与が必要になります。

さらに、アノテーションは付与しなくてもよいですが、setterメソッドの定義が必要になります。

そのため、 @XlsSheetName を指定する際にはフィールドに付与することをお薦めします。

コード - 3.2.2 メソッドにアノテーションを付与する場合(読み込み時)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 読み込み時は、setterメソッドに付与する。
@XlsSheet(name="Users")
public class SheetObject {

    private String sheetName;

    // 読み込み時は、setterメソッドにアノテーションの付与が必要。
    @XlsSheetName
    public void setSheetName(String sheetName) {
        return sheetName;
    }

}
コード - 3.2.3 メソッドにアノテーションを付与する場合(書き込み時)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 書き込み時は、getterメソッドに付与し、かつsetterメソッドの定義が必要。
@XlsSheet(name="Users")
public class SampleSheet {

    private String sheetName;

    // 書き込み時は、getterメソッドにアノテーションの付与が必要。
    @XlsSheetName
    public String getSheetName() {
        return sheetName;
    }

    // アノテーションの付与は必要ないが、定義が必要。
    public void setSheetName(String sheetName) {
        return sheetName;
    }

}

3.3. @XlsCell

セルの列と行を指定してBeanのプロパティにマッピングします。

フィールドまたはメソッドに対して付与します。

  • 属性 columnrow で、インデックスを指定します。

    • columnは列番号で、0から始まります。

    • rowは行番号で、0から始まります。

  • 属性 address で、 'B3' のようにシートのアドレス形式で指定もできます。

    • 属性addressを指定する場合は、column, rowは指定しないでください。

    • 属性addressの両方を指定した場合、addressの値が優先されます。

_images/Cell.png

図 - 3.3.1 Cell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@XlsSheet(name="Users")
public class SampleSheet {

    // インデックス形式で指定する場合
    @XlsCell(column=0, row=0)
    private String title;

    // アドレス形式で指定する場合
    @XlsCell(address="B3")
    private String name;

}

3.4. @XlsLabelledCell

セルの見出し用のラベルセルを指定し、その左右もしくは下側のセルの値をマッピングします。

フィールドまたはメソッドに対して付与します。

  • 属性 label で、見出しとなるセルの値を指定します。

  • 属性 type で、見出しセルから見て値が設定されている位置を指定します。

    • 列挙型 LabelledCellType で、左右もしくは下側のセルを指定できます。

  • 属性 optional で、見出しとなるセルが見つからない場合に無視するかどうかを指定しできます。

_images/LabelledCell.png

図 - 3.4.1 LabelledCell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledCell(label="ラベル1", type=LabelledCellType.Right)
    private String title;

    // ラベルセルが見つからなくても処理を続行する
    @XlsLabelledCell(label="ラベル2", type=LabelledCellType.Bottom, optional=true)
    private String summary;
}

3.4.1. ラベルセルから離れたセルを指定する方法(属性range)

属性 range を指定すると、属性typeの方向に向かって指定した セル数分を検索 し、最初に発見した空白以外のセルの値を取得します。

  • 属性 rangeskip を同時に指定した場合、まず、skip分セルを読み飛ばし、そこからrangeの範囲で空白以外のセルを検索します。

  • 属性 range は、 読み込み時のみ有効 です。書き込み時に指定しても無視されます。

_images/LabelledCell_range.png

図 - 3.4.2 LabelledCell(range)

1
2
3
4
5
6
7
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledCell(label="ラベル", type=LabelledCellType.Right, range=4)
    private String title;

}

3.4.3. ラベルセルが結合している場合(属性labelMerged)

  • 属性 labelMerged で、見出しのラベルセルが結合を考慮するか指定します。 [ver.2.0+]

    • trueのときは、結合されているセルを1つのラベルセルとしてマッピングします。

    • falseの場合は、結合されていても解除した状態と同じマッピング結果となります。

    • 初期値はtrueであるため、特に意識はする必要はありません。

  • 属性 labelMerged の値がfalseのとき、ラベルセルが結合されていると、値が設定されているデータセルまでの距離が変わるため、属性 skip を併用します。

_images/LabelledCell_labelMerged.png

図 - 3.4.4 LabelledCell(labelMerged)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@XlsSheet(name="Users")
public class SampleSheet {

    // labelMerged=trueは初期値なので、省略可
    @XlsLabelledCell(label="ラベル1", type=LabelledCellType.Right)
    private String title1;

    // labelMerged=falseで、ラベルが結合しているときは、skip属性を併用します。
    @XlsLabelledCell(label="ラベル2", type=LabelledCellType.Right, labelMerged=false, skip=2)
    private String title2;

}

3.4.4. ラベルセルが重複するセルを指定する方法

同じラベルのセルが複数ある場合は、区別するための見出しを属性 headerLabel で指定します。

属性headerLabelで指定したセルから、label属性で指定したセルを下方向に検索し、最初に見つかった一致するセルをラベルセルとして使用します。

_images/LabelledCell_headerLabel.png

図 - 3.4.5 LabelledCell(headerLabel)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledCell(label="クラス名", type=LabelledCellType.Right,
            headerLabel="アクション")
    private String actionClassName;

    @XlsLabelledCell(label="クラス名", type=LabelledCellType.Right,
            headerLabel="アクションフォーム")
    private String formClassName;

}

3.4.5. ラベルセルを正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し、正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、label , headerLabel です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// シート用クラス
@XlsSheet(name="Users")
public class SampleSheet {

    // 正規表現による指定
    @XlsLabelledCell(label="/名前.+/", type=LabelledCellType.Right)
    private String className;

}

3.5. @XlsArrayCells

連続し隣接するセルをCollection(List, Set)または配列にマッピングします。 [ver.2.0+]

  • 属性 columnrow で、セルの位置をインデックスで指定します。

    • columnは列番号で、0から始まります。

    • rowは行番号で、0から始まります。

  • 属性 address で、 'B3' のようにシートのアドレス形式で指定もできます。

    • 属性addressを指定する場合は、column, rowは指定しないでください。

    • 属性addressの両方を指定した場合、addressの値が優先されます。

  • 属性 direction で、連続する隣接するセルの方向を指定します。

    • 列挙型 ArrayDirection で、横方向(右方向)もしくは、直方向(下方向)を指定できます。

    • 初期値は、横方向(右方向)です。

  • 属性 size で、連続するセルの個数を指定します。

  • Collection(List, Set)型または配列のフィールドに付与します。

    • List型などの場合、Genericsのタイプとして、マッピング先のクラスを指定します。

    • 指定しない場合は、アノテーションの属性 elementClass でクラス型を指定します。

_images/ArrayCells.png

図 - 3.5.1 ArrayCells

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@XlsSheet(name="Users")
public class SampleSheet {

    // インデックス形式、横方向で指定する場合
    // 属性directionを省略した場合は、ArrayDirection.Horizonを指定したと同じ意味。
    @XlsArrayCells(column=0, row=0, size=6)
    private List<String> nameKanas1;

    // アドレス形式、配列にマッピング
    @XlsArrayCells(address="A1", size=6, direction=ArrayDirection.Horizon)
    private String[] nameKanas2;

}

3.5.1. 縦方向に隣接したセルをマッピングする場合

  • 縦方向にマッピングするため、属性 direction を、ArrayDirection.Vertical に設定します。

_images/ArrayCells_direction.png

図 - 3.5.2 ArrayCells(direction)

1
2
3
4
5
6
7
8
@XlsSheet(name="Users")
public class SampleSheet {

    // 縦方向の隣接するセル
    // 属性direction=ArrayDirection.Verticalを指定すると、縦方向にマッピングします。
    @XlsLabelledArrayCells(address="B3", direction=ArrayDirection.Vertical, size=4)
    private List<String> names;
}

3.5.2. 結合したセルをマッピングする場合

  • 属性 elementMerged で、セルの結合を考慮するか指定します。

    • trueのときは、結合されているセルを1つのセルとしてマッピングします。

    • falseの場合は、結合されていても解除した状態と同じマッピング結果となります。

      • ただし、falseのときは、書き込む際には結合が解除されます。

    • 初期値はtrueであるため、特に意識はする必要はありません。

  • セルが結合されている場合は、結合後の個数を指定します。

_images/ArrayCells_elementMerged.png

図 - 3.5.3 ArrayCells(elementMerged)

1
2
3
4
5
6
7
8
@XlsSheet(name="Users")
public class SampleSheet {

    // elementMerged=trueは初期値なので、省略可
    @XlsArrayCells(address="B3", size=3, elementMerged=true)
    private List<String> words;

}

3.5.3. 書き込み時に配列・リストのサイズが不足、または余分である場合

アノテーション @XlsArrayOption を指定することで、書き込み時のセルの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が小さく、足りない場合の操作を指定します。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が大きく、余っている場合の操作を指定します。

_images/ArrayColumns_ArrayOption.png

図 - 3.5.4 ArrayCells(ArrayOption)

1
2
3
4
5
6
7
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsArrayCells(address="B3", size=6)
    @XlsArrayOption(overOperation=OverOperation.Error, remainedOperation=RemainedOperation.Clear)
    private List<String> nameKana;
}

3.5.4. 位置情報/見出し情報を取得する際の注意事項

マッピング対象のセルのアドレスを取得する際に、フィールド Map<String, Point> positions を定義しておけば、自動的にアドレスがマッピングされます。

通常は、キーにはプロパティ名が記述(フィールドの場合はフィールド名)が入ります。

アノテーション @XlsArrayCells でマッピングしたセルのキーは、 <プロパティ名>[<インデックス>] の形式になります。 インデックスは、0から始まります。

マッピング対象の見出しを取得する、フィールド Map<String, String> labels は、見出しはないため取得することはできません。

_images/ArrayCells_positions.png

図 - 3.5.5 XlsArrayCells(positions/labels)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class SampleRecord {

    // 位置情報
    private Map<String, Point> positions;

    // 見出し情報
    private Map<String, String> labels;

    @XlsArrayCells(address="B3", size=6)
    private List<String> nameKana;
}

// 位置情報・見出し情報へのアクセス
SampleRecord record = /* レコードのインスタンスの取得 */;

Point position = record.positions.get("nameKana[2]");

// 見出しは存在しないので、nullが返される
String label = recrod.labeles.get("nameKana[2]");

3.6. @XlsLabelledArrayCells

セルの見出し用のラベルセルを指定し、左右もしくは下側に連続し隣接するセルをCollection(List, Set)または配列にマッピングします。 [ver.2.0+]

@XlsArrayCells@XlsLabelledCell を融合したアノテーションとなります。

  • 属性 label で、見出しとなるセルの値を指定します。

  • 属性 type で、見出しセルから見て値が設定されている位置を指定します。

    • 列挙型 LabelledCellType で、左右もしくは下側のセルを指定できます。

  • 属性 direction で、連続する隣接するセルの方向を指定します。

    • 列挙型 ArrayDirection で、横方向(右方向)もしくは、直方向(下方向)を指定できます。

    • 初期値は、横方向(右方向)です。

    • ただし、セルの位置を左側( type=LabelledCellType.Left ) とした場合、セルの方向は横方向( direction=ArrayDirection.Horizon ) は、設定できないため注意してください。

  • 属性 size で、連続するセルの個数を指定します。

  • 属性 optional で、見出しとなるセルが見つからない場合に無視するかどうかを指定しできます。

  • Collection(List, Set)型または配列のフィールドに付与します。

    • List型などの場合、Genericsのタイプとして、マッピング先のクラスを指定します。

    • 指定しない場合は、アノテーションの属性 elementClass でクラス型を指定します。

_images/LabelledArrayCells.png

図 - 3.6.1 LabelledArrayCells

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@XlsSheet(name="Users")
public class SampleSheet {

    // ラベルの右側 + 横方向の隣接するセル
    // 属性directionを省略した場合は、ArrayDirection.Horizonを指定したと同じ意味。
    @XlsLabelledArrayCells(label="ラベル1", type=LabelledCellType.Right, size=6)
    private List<String> nameKanas1;

    // ラベルの下側 + 横方向の隣接するセル
    // 属性optional=trueと設定すると、ラベルセルが見つからなくても処理を続行する
    @XlsLabelledArrayCells(label="ラベル2", type=LabelledCellType.Bottom,
             direction=ArrayDirection.Horizon, size=6, optional=true)
    private String[] nameKanas2;

}

3.6.1. 縦方向に隣接したセルをマッピングする場合

  • 縦方向にマッピングするため、属性 direction を、ArrayDirection.Vertical に設定します。

_images/LabelledArrayCells_direction.png

図 - 3.6.2 LabelledArrayCells(direction)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@XlsSheet(name="Users")
public class SampleSheet {

    // ラベルの右側 + 縦方向の隣接するセル
    // 属性direction=ArrayDirection.Verticalを指定すると、縦方向にマッピングします。
    @XlsLabelledArrayCells(label="ラベル3", type=LabelledCellType.Right,
             direction=ArrayDirection.Vertical, size=4)
    private List<String> nameKanas3;

    // ラベルの下側 + 縦方向の隣接するセル
    @XlsLabelledArrayCells(label="ラベル4", type=LabelledCellType.Right,
             direction=ArrayDirection.Vertical, size=4)
    private String[] nameKanas4 nameKanas4;
}

3.6.2. ラベルセルから離れたセルを指定する方法(属性range)

属性 range を指定すると、属性typeの方向に向かって指定した セル数分を検索 し、最初に発見した空白以外のセルを開始位置としてマッピングします。

  • 属性 rangeskip を同時に指定した場合、まず、skip分セルを読み飛ばし、そこからrangeの範囲で空白以外のセルを検索します。

  • 属性 range は、 読み込み時のみ有効 です。書き込み時に指定しても無視されます。

  • ラベルセルから離れたセルを指定する場合に使用します。

  • ただし、データセルが偶然空白のときは、マッピング対象のセルがずれるため、この属性を使用する場合は注意が必要です。

_images/LabelledArrayCells_range.png

図 - 3.6.3 LabelledArrayCells(range)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledArrayCells(label="ラベル1", type=LabelledCellType.Right, range=4, size=4)
    private List<String> words1;

    @XlsLabelledArrayCells(label="ラベル2", type=LabelledCellType.Bottom, range=5, size=3)
    private List<String> words2;

}

3.6.3. ラベルセルから離れたセルを指定する方法(属性skip)

属性 skip を指定すると、属性typeの方向に向かって、ラベルセルから指定した セル数分離れた セルを開始位置としてマッピングします。

  • 属性 rangeskip を同時に指定した場合、まず、skip分セルを読み飛ばし、そこからrangeの範囲で空白以外のセルを検索します。

_images/LabelledArrayCells_skip.png

図 - 3.6.4 LabelledArrayCells(skip)

1
2
3
4
5
6
7
8
9
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledArrayCells(label="ラベル1", type=LabelledCellType.Right, size=3, skip=2)
    private List<String> words1;

     @XlsLabelledArrayCells(label="ラベル2", type=LabelledCellType.Bottom, size=3, skip=3)
    private List<String> words2;
}

3.6.4. 重複するラベルを指定する場合

同じラベルのセルが複数ある場合は、区別するため見出しを属性 headerLabel で指定します。

属性headerLabelで指定したセルから、label属性で指定したセルを下方向に検索し、最初に見つかった一致するセルをラベルセルとして使用します。

_images/LabelledArrayCells_headerLabel.png

図 - 3.6.5 LabelledArrayCells(headerLabel)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledArrayCells(label="ふりがな", type=LabelledCellType.Right, size=10
            headerLabel="氏名")
    private List<String> nameRuby;

    @XlsLabelledArrayCells(label="ふりがな", type=LabelledCellType.Right, size=10
            headerLabel="住所")
    private List<String> addressRuby;

}

3.6.5. ラベルセルが結合している場合(属性labelMerged)

  • 属性 labelMerged で、見出しのラベルセルが結合を考慮するか指定します。

    • trueのときは、結合されているセルを1つのラベルセルとしてマッピングします。

    • falseの場合は、結合されていても解除した状態と同じマッピング結果となります。

    • 初期値はtrueであるため、特に意識はする必要はありません。

  • 属性 labelMerged の値がfalseのとき、ラベルセルが結合されていると、値が設定されているデータセルまでの距離が変わるため、属性 skip を併用します。

_images/LabelledArrayCells_labelMerged.png

図 - 3.6.6 LabelledArrayCells(labelMerged)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@XlsSheet(name="Users")
public class SampleSheet {

    // labelMerged=trueは初期値なので、省略可
    @XlsLabelledArrayCells(label="ラベル1", type=LabelledCellType.Right, size=6)
    private List<String> name1;

    // labelMerged=falseで、ラベルが結合しているときは、skip属性を併用します。
    @XlsLabelledArrayCells(label="ラベル2", type=LabelledCellType.Right, size=6,
             labelMerged=false, skip=2)
    private List<String> name2;

}

3.6.6. 結合したセルをマッピングする場合(属性elementMerged)

  • 属性 elementMerged で、セルの結合を考慮するか指定します。

    • trueのときは、結合されているセルを1つのセルとしてマッピングします。

    • falseの場合は、結合されていても解除した状態と同じマッピング結果となります。

      • ただし、falseのときは、書き込む際には結合が解除されます。

    • 初期値はtrueであるため、特に意識はする必要はありません。

  • セルが結合されている場合は、結合後の個数を指定します。

_images/LabelledArrayCells_elementMerged.png

図 - 3.6.7 LabelledArrayCells(elementMerged)

1
2
3
4
5
6
7
8
9
@XlsSheet(name="Users")
public class SampleSheet {

    // elementMerged=trueは初期値なので、省略可
    @XlsLabelledArrayCells(label="ラベル1", type=LabelledCellType.Right, size=3,
            elementMerged=true)
    private List<String> words;

}

3.6.7. 書き込み時に配列・リストのサイズが不足、または余分である場合

アノテーション @XlsArrayOption を指定することで、書き込み時のセルの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が小さく、足りない場合の操作を指定します。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が大きく、余っている場合の操作を指定します。

_images/LabelledArrayCells_ArrayOption.png

図 - 3.6.8 LabelledArrayCells(ArrayOption)

1
2
3
4
5
6
7
8
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledArrayCells(label="ふりがな", type=LabelledCellType.Right, size=6)
    @XlsArrayOption(overOperation=OverOperation.Error, remainedOperation=RemainedOperation.Clear)
    private List<String> nameKana;

}

3.6.8. 位置情報/見出し情報を取得する際の注意事項

マッピング対象のセルのアドレスを取得する際に、フィールド Map<String, Point> positions を定義しておけば、自動的にアドレスがマッピングされます。

通常は、キーにはプロパティ名が記述(フィールドの場合はフィールド名)が入ります。

アノテーション @XlsLabelledArrayCells でマッピングしたセルのキーは、 <プロパティ名>[<インデックス>] の形式になります。 インデックスは、0から始まります。

同様に、マッピング対象の見出しを取得する、フィールド Map<String, String> labels へのアクセスも、キーは、 <プロパティ名>[<インデックス>] の形式になります。 ただし、見出し情報の場合は、全ての要素が同じ値になるため、従来通りの <プロパティ名> でも取得できます。

_images/LabelledArrayCells_positions.png

図 - 3.6.9 LabelledArrayCells(positions/labels)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SampleRecord {

    // 位置情報
    private Map<String, Point> positions;

    // 見出し情報
    private Map<String, String> labels;

    @XlsLabelledArrayCells(label="ふりがな", type=LabelledCellType.Right, size=6)
    private List<String> nameKana;

}

// 位置情報・見出し情報へのアクセス
SampleRecord record = /* レコードのインスタンスの取得 */;

Point position = record.positions.get("nameKana[2]");

String label = recrod.labeles.get("nameKana[2]");

// 見出し情報の場合、従来通りのインデックスなしでも取得できる
String label = recrod.labeles.get("nameKana");

3.6.9. ラベルセルを正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し、正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、label , headerLabel です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// シート用クラス
@XlsSheet(name="Users")
public class SampleSheet {

    // 正規表現による指定
    @XlsLabelledArrayCells(label="/名前.+/", type=LabelledCellType.Right, size=10)
    private List<String> names;

}

3.7. @XlsHorizontalRecords

水平方向に連続する行をCollection(List、Set)または配列にマッピングします。

標準では表には最上部に表の名称と列名を記述した行が必要になります。

_images/HorizontalRecord.png

図 - 3.7.1 HorizontalRecords

シート用クラスに、アノテーション @XlsHorizontalRecords 使って定義し、属性tableLabelで表の名称を指定します。

Colelction(List, Set)型または配列のフィールドに付与します。 List型などの場合、Genericsのタイプとして、マッピング先のBeanクラスを指定します。 指定しない場合は、アノテーションの属性 recordClass でクラス型を指定します。

レコード用クラスは、列の定義をアノテーション @XlsColumn@XlsMapColumns で指定します。 また、ツリー構造のように入れ子になったレコードをマッピングする場合は、 @XlsNestedRecords を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// シート用クラス
@XlsSheet(name="Users")
public class SampleSheet {

    // 通常は、Genericsでクラス型を指定します。
    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    private List<UserRecord> records;

    // Generics型を使用しない場合は、属性 recordClass で指定します。
    @XlsHorizontalRecords(tableLabel="ユーザ一覧", recordClass=UserRecord.class)
    private List record2;
}

// レコード用クラス
public class UserRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

}

注釈

  • ver1.0から、Collection型(List型、Set型)にも対応しています。

  • インタフェースの型を指定する場合、次の実装クラスのインスタンスが設定されます。

    • List型の場合、 java.util.ArrayList クラス。

    • Set型の場合、 java.util.LinkedHashSet クラス。

    • Collection型の場合、 java.util.ArrayList クラス。

  • 実装クラスを指定した場合、そのインスタンスが設定されます。

3.7.1. 表の開始位置の指定(表の名称がない場合)

表の名称がない場合、表の開始位置をインデックスやアドレスで指定します。

  • 属性 headerColumnheaderRow で表の開始位置をインデックスで指定します。

    • headerColumnは列番号で、0から始まります。

    • headerRowは行番号で、0から始まります。

  • 属性 headerAddress で、 'B3'のようにシートのアドレス形式で指定もできます。

    • 属性headerAddressを指定する場合は、headerColumn, headerRowは指定しないでください。

    • 属性headerAddressの両方を指定した場合、headerAddressの値が優先されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@XlsSheet(name="Users")
public class SampleSheet {

    // インデックス形式で表の開始位置を指定する(値は0から開始します)
    @XlsHorizontalRecords(headerColumn=0, headerRow=1)
    private List<UserRecord> records1;

    // アドレス形式で表の開始位置を指定する場合
    @XlsHorizontalRecords(headerAddress="A2")
    private List<UserRecord> records2;
}

3.7.2. 表の名称から開始位置が離れた場所にある場合

表の名称が定義してあるセルの直後に表がなく離れている場合、属性 bottom で表の開始位置がどれだけ離れているか指定します。

_images/HorizontalRecord_bottom.png

図 - 3.7.2 HorizontalRecords(bottom)

1
2
3
4
5
6
7
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧", bottom=3)
    private List<UserRecord> records;

}

3.7.3. 表の見出しが縦に結合されデータレコードの開始位置が離れた場所にある場合

表の見出しセルが縦に結合され、データレコードの開始位置が離れている場合、属性 headerBottom でデータレコードの開始位置がどれだけ離れているか指定します。 [ver1.1+]

下記の例の場合、見出しの「テスト結果」は横に結合されているため @XlsColumn(headerMerged=N) と組み合わせて利用します。

_images/HorizontalRecord_headerBottom.png

図 - 3.7.3 HorizontalRecords(headerBottom)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// シート用クラス
@XlsSheet(name="Users")
public class SampleSheet {

    // 見出しが縦に結合され、データのレコードの開始位置が離れている場合
    @XlsHorizontalRecords(tableLabel="クラス情報", headerBottom=2)
    private List<SampleRecord> records;

}

// レコード用クラス
public class SampleRecord {

    @XlsColumn(columnName="No.")
    private int no;

    @XlsColumn(columnName="名前")
    private String name;

    // セル「算数」のマッピング
    @XlsColumn(columnName="テスト結果")
    private int sansu;

    // セル「国語」のマッピング
    // 結合されている見出しから離れている数を指定する
    @XlsColumn(columnName="テスト結果", headerMerged=1)
    private int kokugo;

    // セル「合計」のマッピング
    // 結合されている見出しから離れている数を指定する
    @XlsColumn(columnName="テスト結果", headerMerged=2)
    private int sum;

}

3.7.4. 表の終端の指定(属性terminal)

デフォルトでは行に1つもデータが存在しない場合、その表の終端となります。 行の一番左側の列の罫線によってテーブルの終端を検出する方法もあります。 この場合は @XlsHorizontalRecords の属性 terminalRecordTerminal.Border を指定してください。

_images/HorizontalRecord_terminal.png

図 - 3.7.4 HorizontalRecords(terminal)

1
2
3
4
5
6
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧", terminal=RecordTerminal.Border)
    private List<UserRecord> records;
}

注釈

書き込む際にはテンプレート用の表中のセルは空と記述しているため、属性 terminal=RecordTermial.Empty を指定していると処理が終了してしまいます。 そのため、強制的に terminal=RecordTerminal.Border に補正して処理するようにしています。[ver0.5+]

3.7.5. 空のレコードを読み飛ばす条件の指定

レコード用のクラスには、レコードを宇読み飛ばすかどうか判定するためのメソッド用意し、アノテーション @XlsIgnorable を付与します。

また、この属性は読み込み時のみに有効です。書き込み時は、空のレコードでもそのまま出力されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ルートのオブジェクト
@XlsSheet(name="シート名")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧", terminal=RecordTerminal.Border)
    private List<UserRecord> users;
}

// レコードのオブジェクト
public class UserRecord {

    @XlsColumn(columnName="名前")
    private String name;

    // レコードが空と判定するためのメソッド
    @XlsIgnorable
    public boolean isEmpty() {

        if(name != null || !name.isEmpty()) {
            return false;
        }

        return true;
    }
}

3.7.6. 表の終端の指定(属性terminateLabel)

表が他の表と連続しており属性terminalでBorder、Emptyのいずれを指定しても終端を検出できない場合があります。 このような場合は、属性 terminateLabel で終端を示すセルの文字列を指定します。

_images/HorizontalRecord_terminateLabel.png

図 - 3.7.5 HorizontalRecords(terminateLabel)

1
2
3
4
5
6
7
8
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="クラス情報", terminal=RecordTerminal.Border,
            terminateLabel="平均")
    private List<UserRecord> userRecords;

}

3.7.7. 表の見出しの走査の終了条件の指定(headerLimit)

属性 headerLimit を指定すると、テーブルのカラムが指定数見つかったタイミングでExcelシートの走査を終了します。 主に無駄な走査を抑制したい場合に指定します。

例えば、@XlsIterateTables において、 テーブルが隣接しており終端を検出できないときに、カラム数を明示的に指定してテーブルを区切りたい場合に使用します。

以下の例は、列の見出しセルを3つ分検出したところでそのテーブルの終端と見なします。

_images/HorizontalRecord_headerLimit.png

図 - 3.7.6 HorizontalRecords(headerLimit)

1
2
3
4
5
6
7
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="クラス情報", terminal=RecordTerminal.Border,
            headerLimit=3)
    private List<UserRecord> records;
}

なお、セルが見つからなかった場合はエラーとなりますが、属性 optional にtrueを指定しておくと、無視して処理を続行します。

3.7.8. 表の見出しに空白がある場合(range)

表の走査は、まず指定したタイトルなどの表の開始位置を元に、見出し用セルを取得し、その後、データのレコードを取得します。

見出し用セルを取得する際には、右方向に向かって検索をしますが、 通常は空白セルが見つかった時点で走査を終了 します。

空白セルの次にも見出し用セルがあるような場合、属性 range を指定することで、指定した値分の空白セルを許容し、 さらに先のセルの検索を試みます。

また、属性 headerAddresstableLabel で指定した位置から表が開始しないような場合も、 属性 range を指定することで、さらに先のセルの検索を試みます。

_images/HorizontalRecord_range.png

図 - 3.7.7 HorizontalRecords(range)

1
2
3
4
5
6
7
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧", terminal=RecordTerminal.Border,
            range=3)
    private List<UserRecord> records;
}

3.7.9. 書き込み時にレコードが不足、余分である場合の操作の指定

アノテーション @XlsRecordOption を指定することで、書き込み時のレコードの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトのレコード数に対して、シートのレコード数が足りないときの操作を指定します。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトのレコード数に対して、シートのレコード数が余っているときの操作を指定します。

_images/HorizontalRecord_RecordOption.png

図 - 3.7.8 HorizontalRecords(RecordOption)

1
2
3
4
5
6
7
8
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    @XlsRecordOption(overOperation=OverOperation.Insert, remainedOperation=RemainedOperation.Clear)
    private List<UserRecord> records;

}

3.7.10. 任意の位置からレコードが開始するかを指定する場合

データレコードの途中で中見出しがあり、分割されているような表の場合、アノテーション @XlsRecordFinder で、レコードの開始位置を決める処理を指定することができます。 [ver2.0+]

  • 属性 value で、レコードの開始位置を検索する実装クラスを指定します。

  • 属性 args で、レコードの開始位置を検索する実装クラスに渡す引数を指定します。

_images/HorizontalRecord_RecordFinder.png

図 - 3.7.9 HorizontalRecords(RecordFinder)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// マッピングの定義
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsOrder(1)
    @XlsHorizontalRecords(tableLabel="成績一覧", bottom=2, terminal=RecordTerminal.Border, terminateLabel="/クラス.+/")
    @XlsRecordFinder(value=ClassNameRecordFinder.class, args="クラスA")
    private List<Record> classA;

    @XlsOrder(2)
    @XlsHorizontalRecords(tableLabel="成績一覧", bottom=2, terminal=RecordTerminal.Border, terminateLabel="/クラス.+/")
    @XlsRecordFinder(value=ClassNameRecordFinder.class, args="クラスB")
    private List<Record> classB;

}

// クラス用の見出しのレコードを探すクラス
public class ClassNameRecordFinder implements RecordFinder {

    @Override
    public CellPosition find(ProcessCase processCase, String[] args, Sheet sheet,
            CellPosition initAddress, Object beanObj, Configuration config) {

        // 実装は省略
    }

}

3.7.11. 表の名称を正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、tableLabel , terminateLabel です。 さらに、レコードの列の見出し @XlsColumn も、この機能が有効になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// シート用クラス
@XlsSheet(name="Users")
public class SampleSheet {

    // 正規表現による指定
    @XlsHorizontalRecords(tableLabel="/ユーザ一覧.+/")
    private List<UserRecord> records;

}

3.8. @XlsVerticalRecords

垂直方向に連続する列をListまたは配列にマッピングします。 要するに @XlsHorizontalRecords を垂直方向にしたものです。

メソッドに定義する場合、@XlsHorizontalRecords と同じくList型の引数を1つだけ取るsetterメソッドに対して付与します。

ここでは、アノテーション @XlsHorizontalRecords と異なる部分を説明します。 共通の使い方は、アノテーション @XlsHorizontalRecords の説明を参照してください。

_images/VerticalRecord.png

図 - 3.8.1 VerticalRecords

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// シート用クラス
@XlsSheet(name="Weather")
public class SampleSheet {

    @XlsVerticalRecords(tableLabel="天気情報")
    private List<WeatherRecord> records;

}

// レコード用クラス
public class WeatherRecord {

    @XlsColumn(columnName="時間")
    private String time;

    @XlsColumn(columnName="降水")
    private double precipitation;
}

3.8.1. 表の名称位置の指定

実際に表を作る場合、垂直方向ですが表の名称は上方に設定することが一般的です。 そのような場合、属性 tableLabelAbove の値を true に設定すると表のタイトルが上方に位置するとして処理します。 [ver1.0+]

_images/VerticalRecord_tableLabelAbove.png

図 - 3.8.2 VerticalRecords(tableLabelAbove)

1
2
3
4
5
6
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsVerticalRecords(tableLabel="天気情報", tableLabelAbove=true)
    private List<WeatherRecord> records;
}

3.8.3. 表の名称から開始位置が離れた場所にある場合(bottom)

属性 tableLabelAbove の値が true のときのみ有効になります。 表の名称がセルの直後に表がなく離れている場合、属性 bottom で表の開始位置が 下方向 にどれだけ離れているか指定します。 [ver2.0]+

アノテーション @XlsHorizontalRecords の属性 bottom と同じような意味になります。

_images/VerticalRecord_bottom.png

図 - 3.8.4 VerticalRecords(bottom)

1
2
3
4
5
6
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsVerticalRecords(tableLabel="天気情報", tableLabelAbove=true, bottom=3)
    private List<WeatherRecord> records;
}

3.8.4. 表の見出しが横に結合されデータレコードの開始位置が離れた場所にある場合

表の見出しセルが横に結合され、データレコードの開始位置が離れている場合、属性 headerRight でデータレコードの開始位置がどれだけ離れているか指定します。 [ver1.1+]

アノテーション @XlsHorizontalRecords の属性 headerBottom と同じような意味になります。

下記の例の場合、見出しの「テスト結果」は横に結合されているため @XlsColumn(headerMerged=N) と組み合わせて利用します。

_images/VerticalRecord_headerRight.png

図 - 3.8.5 VerticalRecords(headerRight)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// シート用クラス
@XlsSheet(name="Weather")
public class SampleSheet {

    // 見出しが横に結合され、データのレコードの開始位置が離れている場合
    @XlsVerticalRecords(tableLabel="天気情報", headerRight=2)
    private List<WeatherRecord> records;

}

// レコード用クラス
public class WeatherRecord {

    @XlsColumn(columnName="時間")
    private String time;

    // セル「降水」のマッピング
    @XlsColumn(columnName="測定結果")
    private double precipitation;

    // セル「気温」のマッピング
    // 結合されている見出しから離れている数を指定する
    @XlsColumn(columnName="測定結果", headerMerged=1)
    private int temperature;

    // セル「天気」のマッピング
    // 結合されている見出しから離れている数を指定する
    @XlsColumn(columnName="測定結果", headerMerged=2)
    private String wather;

}

3.8.5. 書き込み時にレコードが不足、余分である場合の操作の指定

アノテーション @XlsRecordOption を指定することで、書き込み時のレコードの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトのレコード数に対して、シートのレコード数が足りないときの操作を指定します。

    • ただし、 @XlsVerticalRecords の場合、列の挿入を行う OverOperation#Insert は使用できません。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトのレコード数に対して、シートのレコード数が余っているときの操作を指定します。

    • ただし、 @XlsVerticalRecords の場合、列の削除を行う RemainedOperation#Delete は使用できません。

_images/VerticalRecord_RecordOption.png

図 - 3.8.6 VerticalRecords(RecordOption)

1
2
3
4
5
6
7
8
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsVerticalRecords(tableLabel="天気情報")
    @XlsRecordOption(overOperation=OverOperation.Copy, remainedOperation=RemainedOperation.Clear)
    private List<WeatherRecord> records;

}

3.8.6. 任意の位置からレコードが開始するかを指定する場合

データレコードの途中で中見出しがあり、分割されているような表の場合、アノテーション @XlsRecordFinder で、レコードの開始位置を決める処理を指定することができます。 [ver2.0+]

  • 属性 value で、レコードの開始位置を検索する実装クラスを指定します。

  • 属性 args で、レコードの開始位置を検索する実装クラスに渡す引数を指定します。

_images/VerticalRecord_RecordFinder.png

図 - 3.8.7 VerticalRecords(RecordFinder)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// マッピングの定義
@XlsSheet(name="Weather")
public class SampleSheet {

    @XlsOrder(1)
    @XlsVerticalRecords(tableLabel="天気情報", tableLabelAbove=true, terminal=RecordTerminal.Border, terminateLabel="/{0-9}月{0-9}[1-2]日/")
    @XlsRecordFinder(value=DateRecordFinder.class, args="2月1日")
    private List<WeatherRecord> date1;

    @XlsOrder(2)
    @XlsVerticalRecords(tableLabel="天気情報", tableLabelAbove=true, terminal=RecordTerminal.Border, terminateLabel="/{0-9}月{0-9}[1-2]日/")
    @XlsRecordFinder(value=DateRecordFinder.class, args="2月1日")
    private List<WeatherRecord> date2;

}

// 日にち用の見出しのレコードを探すクラス
public class DateRecordFinder implements RecordFinder {

    @Override
    public CellPosition find(ProcessCase processCase, String[] args, Sheet sheet,
            CellPosition initAddress, Object beanObj, Configuration config) {

        // 実装は省略
    }

}

3.9. @XlsColumn

アノテーション @XlsHorizontalRecords または @XlsVerticalRecords において、 1つのカラムをマッピングします。

  • 属性 columnName で、見出しとなるセルのラベルを指定します。

  • セルが見つからない場合はエラーとなりますが、属性 optionaltrue とすることで無視して処理を続行します。

_images/Column.png

図 - 3.9.1 Column

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")}
    private String name;

    // 存在しない列の場合は読み飛ばす
    @XlsColumn(columnName="備考", optional=true)
    private String name;
}

3.9.1. データの列が結合されている場合

同じ値がグループごとに結合されているカラムの場合は属性 merged をtrueに設定します。 こうしておくと、前の列の値が引き継がれて設定されます。

_images/Column_merged.png

図 - 3.9.2 Column(merged)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    // 結合されてる可能性がある列
    @XlsColumn(columnName="クラス", merged=true)
    private String className;

    @XlsColumn(columnName="名前")
    private String name;

}

注釈

書き込みに時では、属性mergedの値が true であっても、上部または左側のセルと値が同じでも結合は基本的に行いません。 ただし、システム設定 Configuration の項目「mergeCellOnSave」の値をtrueにすると結合されます。

3.9.2. 見出し行が結合されている場合

見出し行が結合され、1つの見出しに対して複数の列が存在する場合は属性 headerMerged を使用します。

属性headerMergedの値には列見出しから何セル分離れているかを指定します。

属性columnNameで指定する見出しのセル名は、結合されているセルと同じ値を指定します。

_images/Column_headerMerged.png

図 - 3.9.3 Column(headerMerged)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsColumn(columnName="連絡先")
    private String mailAddress;

    // 結合されている見出しから離れている数を指定する
    @XlsColumn(columnName="連絡先", headerMerged=1)
    private String tel;

}

3.9.3. 見出しを正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し、正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、columnName です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// レコード用クラス
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    // 正規表現による指定
    @XlsColumn(columnName="/名前.+/")
    private String name;

}

3.10. @XlsMapColumns

アノテーション @XlsHorizontalRecords もしくは @XlsVerticalRecords において、 指定されたレコード用クラスのカラム数が可変の場合に、それらのカラムを java.util.Map として設定します。

  • BeanにはMapを引数に取るフィールドまたはメソッドを用意し、このアノテーションを記述します。

  • 属性 previousColumnName で、指定された次のカラム以降、カラム名をキーとしたMapが生成され、Beanにセットされます。

  • 属性 optional で、見出しとなるセルが見つからない場合に無視するかどうかを指定しできます。 [ver2.0+]

_images/MapColumns.png

図 - 3.10.1 MapColumns

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsMapColumns(previousColumnName="名前")
    private Map<String, String> attendedMap;
}

3.10.1. 終了条件のセルを指定する場合

属性 nextColumnName で、指定した前のカラムまでが処理対象となり、マッピングの終了条件を指定することができます。 [ver1.2+]

  • 属性 optional で、見出しとなるセルが見つからない場合に無視するかどうかを指定しできます。 [ver2.0+]

_images/MapColumns_nextColumnName.png

図 - 3.10.2 MapColumns(nextColumnName)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsMapColumns(previousColumnName="名前", nextColumnName="備考")
    private Map<String, String> attendedMap;

    @XlsColumn(columnName="備考")
    private String comment;

}

3.10.2. 型変換する場合

アノテーション @XlsConverter などで型変換を適用するときは、Mapの値が変換対象となります。 マップのキーは必ずString型を指定してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    // 型変換用のアノテーションを指定した場合、Mapの値に適用されます。
    @XlsMapColumns(previousColumnName="名前")
    @XlsBooleanConverter(loadForTrue={"出席"}, loadForFalse={"欠席"},
            saveAsTrue="出席", saveAsFalse"欠席"
            failToFalse=true)
    private Map<String, Boolean> attendedMap;
}

3.10.3. 位置情報/見出し情報を取得する際の注意事項

マッピング対象のセルのアドレスを取得する際に、フィールド Map<String, Point> positions を定義しておけば、自動的にアドレスがマッピングされます。

通常は、キーにはプロパティ名が記述(フィールドの場合はフィールド名)が入ります。

アノテーション @XlsMapColumns でマッピングしたセルのキーは、 <プロパティ名>[<セルの見出し>] の形式になります。

同様に、マッピング対象の見出しを取得する、フィールド Map<String, String> labels へのアクセスも、 キーは、 <プロパティ名>[<セルの見出し>] の形式になります。

_images/MapColumns_positions.png

図 - 3.10.3 MapColumns(positions/labels)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SampleRecord {

    // 位置情報
    private Map<String, Point> positions;

    // 見出し情報
    private Map<String, String> labels;

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsMapColumns(previousColumnName="名前")
    private Map<String, String> attendedMap;
}

// 位置情報・見出し情報へのアクセス
SampleRecord record = /* レコードのインスタンスの取得 */;

Point position = record.positions.get("attendedMap[4月2日]");

String label = recrod.labeles.get("attendedMap[4月2日]");

3.10.4. 見出しを正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し、正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、previousColumnNamenextColumnName です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// レコード用クラス
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    // 正規表現による指定
    @XlsColumn(columnName="/名前.+/")
    private String name;

    // 正規表現による指定
    @XlsMapColumns(previousColumnName="/名前.+/", nextColumnName="/備考.+/")
    private Map<String, String> attendedMap;

    @XlsColumn(columnName="/備考.+/")
    private String comment;

}

3.10.5. 書き込み前に動的にテンプレートファイルを書き換える

書き込み処理の場合、マップのキーがデータごとに異なり、テンプレートのフォーマットと合わない場合があります。

そのような場合、テンプレートファイルを書き込むデータに合わせて書き換えます。 その際には、 ライフサイクル・コールバック用のアノテーション @XlsPreSave で、実装を行うことができます。

実装処理は、Apache POIのAPIを使って行います。。

_images/MapColumns_preSave.png

図 - 3.10.4 MapColumns(preSave)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// シート用クラス
@XlsSheet(name="List")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    @XlsRecordOption(overOperation=OverOperation.Insert)
    List<SampleRecord> records;

    // XlsMapColumnsのマッピング用のセルを作成する
    @XlsPreSave
    public void onPreSave(final Sheet sheet, final Configuration config) {

        try {
            final Workbook workbook = sheet.getWorkbook();

            // 基準となる日付のセル[日付]を取得する
            Cell baseHeaderCell = Utils.getCell(sheet, "[日付]", 0, 0, config);

            // 書き換えるための見出しの値の取得
            List<String> dateHeaders = new ArrayList<>(records.get(0).attendedMap.keySet());

            // 1つ目の見出しの書き換え
            baseHeaderCell.setCellValue(dateHeaders.get(0));

            // 2つ目以降の見出し列の追加
            Row headerRow = baseHeaderCell.getRow();
            for(int i=1; i < dateHeaders.size(); i++) {
                Cell headerCell = headerRow.createCell(baseHeaderCell.getColumnIndex() + i);

                CellStyle style = workbook.createCellStyle();
                style.cloneStyleFrom(baseHeaderCell.getCellStyle());
                headerCell.setCellStyle(style);
                headerCell.setCellValue(dateHeaders.get(i));

            }

            // 2つめ以降のデータ行の列の追加
            Row valueRow = sheet.getRow(baseHeaderCell.getRowIndex() + 1);
            Cell baseValueCell = valueRow.getCell(baseHeaderCell.getColumnIndex());
            for(int i=1; + i < dateHeaders.size(); i++) {
                Cell valueCell = valueRow.createCell(baseValueCell.getColumnIndex() + i);

                CellStyle style = workbook.createCellStyle();
                style.cloneStyleFrom(baseValueCell.getCellStyle());
                valueCell.setCellStyle(style);

            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}

// レコード用クラス
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    // 可変長のセルのマッピング
    @XlsMapColumns(previousColumnName="名前")
    private Map<String, String> attendedMap;

}

3.11. @XlsArrayColumns

アノテーション @XlsHorizontalRecords もしくは @XlsVerticalRecords において、 指隣接する連続したカラムカラムを、Collection(List, Set)または配列にマッピングします。 [ver.2.0+]

  • 属性 columnName で、見出しとなるセルのラベルを指定します。

  • 属性 size で、連続するセルの個数を指定します。

    • 見出しとなるカラムは、結合している必要があります。

  • 見出しとなるセルが見つからない場合はエラーとなりますが、属性 optionaltrue とすることで無視して処理を続行します。

  • Collection(List, Set)型または配列のフィールドに付与します。

    • List型などの場合、Genericsのタイプとして、マッピング先のクラスを指定します。

    • 指定しない場合は、アノテーションの属性 elementClass でクラス型を指定します。

_images/ArrayColumns.png

図 - 3.11.1 ArrayColumns

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsArrayColumns(columnName="ふりがな", size=10)
    private List<String> nameRuby;
}

3.11.1. 結合したセルをマッピングする場合

  • 属性 elementMerged で、セルの結合を考慮するか指定します。

    • trueのときは、結合されているセルを1つのセルとしてマッピングします。

    • falseの場合は、結合されていても解除した状態と同じマッピング結果となります。

      • ただし、falseのときは、書き込む際には結合が解除されます。

    • 初期値はtrueであるため、特に意識はする必要はありません。

  • セルが結合されている場合は、結合後の個数を指定します。

_images/ArrayColumns_elementMerged.png

図 - 3.11.2 ArrayColumns(elementMerged)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    // elementMerged=trueは初期値なので、省略可
    @XlsArrayColumns(columnName="連絡先", size=3)
    private List<String> contactInfos;
}

3.11.2. 書き込み時に配列・リストのサイズが不足、または余分である場合

アノテーション @XlsArrayOption を指定することで、書き込み時のセルの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が小さく、足りない場合の操作を指定します。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が大きく、余っている場合の操作を指定します。

_images/ArrayColumns_ArrayOption.png

図 - 3.11.3 ArrayColumns(ArrayOption)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsArrayColumns(columnName="ふりがな", size=6)
    @XlsArrayOption(overOperation=OverOperation.Error, remainedOperation=RemainedOperation.Clear)
    private List<String> nameRuby;
}

3.11.3. 位置情報/見出し情報を取得する際の注意事項

マッピング対象のセルのアドレスを取得する際に、フィールド Map<String, Point> positions を定義しておけば、自動的にアドレスがマッピングされます。

通常は、キーにはプロパティ名が記述(フィールドの場合はフィールド名)が入ります。

アノテーション @XlsArrayColumns でマッピングしたセルのキーは、インデックス付きの <プロパティ名>[<インデックス>] 形式になります。 インデックスは、0から始まります。

同様に、マッピング対象の見出しを取得する、フィールド Map<String, String> labels へのアクセスも、キーは、 <プロパティ名>[<インデックス>] の形式になります。 ただし、見出し情報の場合は、全ての要素が同じ値になるため、従来通りの <プロパティ名> でも取得できます。

_images/ArrayColumns_positions.png

図 - 3.11.4 ArrayColumns(positions/labels)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class SampleRecord {

    // 位置情報
    private Map<String, Point> positions;

    // 見出し情報
    private Map<String, String> labels;

    @XlsColumn(columnName="ID")
    private int id;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsArrayColumns(columnName="ふりがな", size=6)
    private List<String> nameRuby;
}

// 位置情報・見出し情報へのアクセス
SampleRecord record = /* レコードのインスタンスの取得 */;

Point position = record.positions.get("nameRuby[2]");

String label = recrod.labeles.get("nameRuby[2]");

// 見出し情報の場合、従来通りのインデックスなしでも取得できる
String label = recrod.labeles.get("nameRuby");

3.11.4. 見出しを正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し、正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、columnName です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// レコード用クラス
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private int id;

    // 正規表現による指定
    @XlsColumn(columnName="/名前.+/")
    private String name;

    // 正規表現による指定
    @XlsArrayColumns(columnName="/ふりがな.+/", size=10)
    private List<String> nameRuby;


}

3.12. @XlsNestedRecords

アノテーション @XlsHorizontalRecords もしくは @XlsVerticalRecords のレコード用クラスにおいて、ツリー構造のように入れ子になっている表をマッピングする際に使用します。 [ver.1.4+]

3.12.1. 一対多の関係

一対多の関係を表現する際には、Collection(List/Set)または、配列で指定します。

  • 親子関係は、結合しているかで表現します。

    • 結合している個数が不一致の場合は、例外 NestedRecordMergedSizeException がスローされます。

  • 親に指定しているJavaBeanクラスは、子や孫には指定することができません。

    • カラムの定義はレコードに記述されているため、親子に同じレコードのクラス定義があると、無限に再帰処理してしまうため、それを防ぐための制約になります。

  • 属性 @XlsHorizotanlRecords#terminalLabel の終端の判定は、入れ子になったレコードごとに判定されます。

  • 読み込みの際、アノテーション @XlsIgnorable で、空のレコードを読み飛ばした結果、レコード数が0件となった場合は、要素数0個リストや配列が設定されます。

_images/NestedRecords_oneToMany.png

図 - 3.12.1 NestedRecords(一対多の関係)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// シート用クラス
@XlsSheet(name="機能")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="機能一覧")
    private List<CategoryRecord> categories;

}

// レコード用クラス(分類)
public class CategoryRecord {

    @XlsColumn(columnName="分類")
    private String name;

    @XlsColumn(columnName="説明(分類)")
    private String description;

    // ネストしたレコードのマッピング
    @XlsNestedRecords
    private List<FunctionRecord> functions;

}

// レコード用クラス(機能)
public class FunctionRecord {

    @XlsColumn(columnName="機能名")
    private String name;

    @XlsColumn(columnName="説明(機能)")
    private String description;

    // ネストしたレコードのマッピング
    @XlsNestedRecords
    private List<DetailRecord> details;

}

// レコード用クラス(詳細)
public class DetailRecord {

    @XlsColumn(columnName="項目")
    private String name;

    @XlsColumn(columnName="値")
    private String value;

}

3.12.2. 一対一の関係

一対一の関係をマッピングする際には、ネストしたクラスを直接指定します。

クラス定義などの制約は、基本的に一対多のときと同じです。

_images/NestedRecords_oneToOne.png

図 - 3.12.2 NestedRecords(一対一の関係)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// シート用クラス
@XlsSheet(name="学期末テスト")
public class SampleSheet {

    @XlsHorizontalRecords(tableLabel="テスト結果", bottom=2)
    private List<UserRecord> users;

}

// レコード用クラス(生徒情報)
public class UserRecord {

    @XlsColumn(columnName="No.")
    private int no;

    @XlsColumn(columnName="クラス", merged=true)
    private String className;

    @XlsColumn(columnName="氏名")
    private String name;

    // ネストしたレコードのマッピング
    @XlsNestedRecords
    private ResultRecord result;

}

// レコード用クラス(テスト結果)
public class ResultRecord {

    @XlsColumn(columnName="国語")
    private int kokugo;

    @XlsColumn(columnName="算数")
    private int sansu;

    @XlsColumn(columnName="合計")
    private int sum;

}

3.13. @XlsIterateTables

同一の構造の表がシート内で繰り返し出現する場合に使用し、Collection(List、Set)または配列にマッピングします。 次のアノテーションを組み合わせて構成します。

  • 横方向の表をマッピングするアノテーション @XlsHorizontalRecords

  • 縦方向の表をマッピングするアノテーション @XlsVerticalRecords [ver.2.0+]

    • ただし、アノテーション @XlsHorizontalRecords と同時に使用することはできません。

  • 見出し付きの1つのセルをマッピングするアノテーション @XlsLabelledCell

  • 見出し付きの連続し隣接する複数のセルをマッピングするアノテーション @XlsLabelledArrayCells [ver.2.0+]

3.13.1. 基本的な使い方

属性 tableLabel で繰り返し部分の表の名称を指定します。

また、属性bottomは、@XlsIterateTables 内で @XlsHorizontalRecords を使用する場合に、 テーブルの開始位置が @XlsIterateTables の表の名称セルからどれだけ離れているかを指定します。

_images/IterateTables.png

図 - 3.13.1 IterateTables

コード - 3.13.1 シート用クラスの定義
1
2
3
4
5
6
@XlsSheet(name="シート名")
public class SampleSheet {

    @XlsIterateTables(tableLabel="部門情報", bottom=2)
    private List<SampleTable> tables;
}

繰り返し部分に対応するJavaBeanでは以下のように、アノテーション @XlsLabelledCell @XlsHorizontalRecords を使用することができます。

アノテーション @XlsHorizontalRecords を使用する場合、属性tableLabel は設定する必要はありません。 @XlsIterateTables の属性 tableLabelとbottomの値を引き継ぐため、指定しなくても問題ないためです。

コード - 3.13.2 テーブル用クラスの定義
1
2
3
4
5
6
7
8
public class SampleTable {

    @XlsLabelledCell(label="部門名", type=LabelledCellType.Right)
    private String deptName;

    @XlsHorizontalRecords(terminal=RecordTerminal.Border)
    private List<SampleRecord> records;
}

繰り返し部分に対応するJavaBeanで @XlsHorizontalRecords を使用した場合、通常の場合と同じく @XlsColumn@XlsMapColumns で列とのマッピングを行います。

コード - 3.13.3 レコード用クラスの定義
1
2
3
4
5
6
7
8
public class SampleRecord {

    @XlsColumn(columnName="ID")
    private String id;

    @XlsColumn(columnName="名前")
    private String name;
}

注釈

  • ver.2.0から、Collection型(List型、Set型)にも対応しています。

  • インタフェースの型を指定する場合、次の実装クラスのインスタンスが設定されます。

    • List型の場合、 java.util.ArrayList クラス。

    • Set型の場合、 java.util.LinkedHashSet クラス。

    • Collection型の場合、 java.util.ArrayList クラス。

  • 実装クラスを指定した場合、そのインスタンスが設定されます。

3.13.2. 縦方向の表を組み合わせてマッピングする場合

縦方向の表をマッピングするアノテーション @XlsVerticalRecords も使用することができます。

  • ただし、横方向の表をマッピングするアノテーション @XlsHorizontalRecords と同時に使用することはできません。

  • 属性 tableLabelAbove=true が自動的に有効になり、表の見出しが上部にあることを前提に処理されます。

_images/IterateTables_VerticalRecords.png

図 - 3.13.2 IterateTables(縦方向)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// シート用クラス
@XlsSheet(name="観測データ")
public class SampleSheet {

    @XlsIterateTables(tableLabel="/観測情報.+/", bottom=2)
    private List<DataTable> tables;
}

// テーブル用クラス
public class DataTable {

    @XlsLabelledCell(label="日付", type=LabelledCellType.Right)
    private LocalDate date;

    @XlsVerticalRecords(terminal=RecordTerminal.Border)
    private List<WeatherRecord> records;
}

// レコード用クラス
public class WeatherRecord {

    @XlsColumn(columnName="時間")
    private String time;

    @XlsColumn(columnName="降水")
    private double precipitation;
}

3.13.3. 表の名称を正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。 [ver1.1+]

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、tableLabel です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// シート用クラス
@XlsSheet(name="シート名")
public class SampleSheet {

    // 正規表現による指定
    @XlsIterateTables(tableLabel="/部門情報.+/", bottom=2)
    private List<SampleTable> tables;

}

3.14. @XlsComment

セルの列と行を指定して、セルのコメント情報をBeanのプロパティにマッピングします。 [ver.2.1+]

フィールドまたはメソッドに対して付与します。

書込み時のコメントの書式の制御は、アノテーション @XlsCommentOption で指定します。

  • 属性 columnrow で、インデックスを指定します。

    • columnは列番号で、0から始まります。

    • rowは行番号で、0から始まります。

  • 属性 address で、 'B3' のようにシートのアドレス形式で指定もできます。

    • 属性addressを指定する場合は、column, rowは指定しないでください。

    • 属性addressの両方を指定した場合、addressの値が優先されます。

_images/Comment.png

図 - 3.14.1 Comment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@XlsSheet(name="Users")
public class SampleSheet {

    // インデックス形式で指定する場合
    @XlsComment(column=0, row=0)
    private String titleComment;

    // アドレス形式で指定する場合
    @XlsComment(address="B3")
    private String titleComment;

}

3.15. @XlsLabelledComment

セルの見出し用のラベルセルを指定し、セルのコメントをマッピングします。 [ver.2.1+]

フィールドまたはメソッドに対して付与します。

書込み時のコメントの書式の制御は、アノテーション @XlsCommentOption で指定します。

  • 属性 label で、見出しとなるセルの値を指定します。

  • 属性 optional で、見出しとなるセルが見つからない場合に無視するかどうかを指定しできます。

_images/LabelledComment.png

図 - 3.15.1 LabelledComment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledComment(label="ラベル1")
    private String titleComment;

    // ラベルセルが見つからなくても処理を続行する
    @XlsLabelledComment(label="ラベル2"optional=true)
    private String summaryComment;
}

3.15.1. ラベルセルが重複するセルを指定する方法

同じラベルのセルが複数ある場合は、区別するための見出しを属性 headerLabel で指定します。

属性headerLabelで指定したセルから、label属性で指定したセルを下方向に検索し、最初に見つかった一致するセルをラベルセルとして使用します。

_images/LabelledComment_headerLabel.png

図 - 3.15.2 LabelledComment(headerLabel)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledComment(label="クラス名", type=LabelledCellType.Right,
            headerLabel="アクション")
    private String actionClassNameComment;

    @XlsLabelledComment(label="クラス名", type=LabelledCellType.Right,
            headerLabel="アクションフォーム")
    private String formClassNameComment;

}

3.15.2. ラベルセルを正規表現、正規化して指定する場合

シートの構造は同じだが、ラベルのセルが微妙に異なる場合、ラベルセルを正規表現による指定が可能です。 また、空白や改行を除去してラベルセルを比較するように設定することも可能です。

  • 正規表現で指定する場合、アノテーションの属性の値を /正規表現/ のように、スラッシュで囲み指定します。

    • スラッシュで囲まない場合、通常の文字列として処理されます。

    • 正規表現の指定機能を有効にするには、システム設定のプロパティ regexLabelText の値を trueに設定します。

  • ラベセルの値に改行が空白が入っている場合、それらを除去し、正規化してアノテーションの属性値と比較することが可能です。

    • 正規化とは、空白、改行、タブを除去することを指します。

    • ラベルを正規化する機能を有効にするには、システム設定のプロパティ normalizeLabelText の値を trueに設定します。

これらの指定が可能な属性は、label , headerLabel です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// システム設定
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration()
        .setRegexLabelText(true)        // ラベルを正規表現で指定可能にする機能を有効にする。
        .setNormalizeLabelText(true);   // ラベルを正規化して比較する機能を有効にする。

// シート用クラス
@XlsSheet(name="Users")
public class SampleSheet {

    // 正規表現による指定
    @XlsLabelledComment(label="/名前.+/")
    private String classNameComment;

}

3.16. @XlsOrder

書き込み時に、@XlsHoriontalRecords を使用して行の挿入や削除を行う設定を行っている場合、 フィールドの処理順序によって、Map<String, Point> positions フィールドで座標がずれる場合があります。

このようなときに、@XlsOrder の属性 value で書き込む処理順序を指定し一定に保つことができます。 属性 value はJavaの仕様により省略が可能です。

@XlsOrder を付与しないフィールドは、付与しているフィールドよりも後から処理が実行されます。 属性valueが同じ値を設定されているときは、 フィールド名の昇順で優先度を決めて処理されます。

_images/Hint.png

図 - 3.16.1 Order

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@XlsSheet(name="Users")
public class SampleSheet {

    // セルの位置情報
    private Map<String, Point> positions;

    @XlsOrder(order=1)
    @XlsHorizontalRecords(tableLabel="ユーザ一覧", terminal=RecordTerminal.Border)
    @XlsRecordOption(overOperation=OverOperation.Insert, remainedOperation=RemainedOperation.Delete)
    private List<UserRecord> records;

    // 属性valueは省略が可能
    @XlsOrder(2)
    @XlsLabelledCell(label="更新日", type=LabelledCellType.Right)
    private Date updateTime;

}

注釈

ソースコード上で定義したフィールドやメソッドの記述順は、実行時には保証されないため、@XlsOrder で順番を指定し、処理順序を一定にすることができます。

@XlsOrder を付与すると、書き込み時だけでなく読み込み時にも処理順序が一定になります。

3.17. @XlsIgnorable

アノテーション @XlsHorizontalRecords@XlsVerticalRecords を使用して、読み込む際に、空のレコードを読み飛ばしたい場合、 レコードが空と判定するためのメソッドに付与します。

  • @XlsIgnorable を付与したメソッドは、publicかつ引数なしの戻り値がboolean型の書式にする必要があります。

  • @XlsVerticalRecords でも同様に使用できます。

また、この機能は読み込み時のみに有効です。書き込み時は、空のレコードでもそのまま出力されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// ルートのオブジェクト
@XlsSheet(name="シート名")
public class UnitUser {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    private List<User> users;

}

// レコードのオブジェクト
public class User {

    @XlsColumn(columnName="No.")
    private int no;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsColumn(columnName="住所")
    private String address;

    // レコードが空と判定するためのメソッド
    @XlsIgnorable
    public boolean isEmpty() {

      if(name != null || !name.isEmpty()) {
        return false;
      }

      if(address != null || !address.isEmpty()) {
        return false;
      }

      return true;
    }
}

3.17.1. IsEmptyBuilderを使った記述の簡単化

IsEmptyBuilder (ver.0.5から追加)を利用することで、より簡潔に記述することも可能です。

  • IsEmptyBuilder#reflectionIsEmpty(...) を利用して判定する場合、位置情報を保持するフィールド Map<String, Point> positions などは除外対象とする必要があります。

  • 独自に判定する場合、IsEmptyBuilder#append(...) を利用します。

  • さらに、 IsEmptyBuilder#compare(IsEmptyComparator) を利用することで独自の判定をすることができます。その際に、Lambda式を利用すると簡潔に記載できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// ルートのオブジェクト
@XlsSheet(name="シート名")
public class UnitUser {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    private List<User> users;

}

// レコードのオブジェクト
public class User {

    // マッピングしたセルの位置情報を保持する。
    private Map<String, Point> positions;

    // マッピングしたセルのラベル情報を保持する。
    private Map<String, String> labels;

    @XlsColumn(columnName="No.")
    private int no;

    @XlsColumn(columnName="名前")
    private String name;

    @XlsColumn(columnName="住所")
    private String address;

    // レコードが空と判定するためのメソッド
    @XlsIgnorable
    public boolean isEmpty() {
        return IsEmptyBuilder.reflectionIsEmpty(this, "positions", "labels");

    }

    // 独自に判定する場合
    public boolean isEmpty2() {
        return new IsEmptyBuilder()
            .append(name)
            .compare(() -> StringUtils.isBlank(address))
            .isEmpty();
    }
}

3.18. @XlsArrayOption

アノテーション @XlsArrayCells@XlsLabelledArrayCells@XlsArrayColumns において、書き込み時の配列・リストの操作を指定するためのアノテーションです。 [ver.2.0+]

3.18.1. 書き込み時に配列・リストのサイズが不足している場合(overOperation)

アノテーション @XlsArrayOption を指定することで、書き込み時のセルの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が小さく、足りない場合の操作を指定します。

    • デフォルト値である列挙型 OverOperation#Break の値のとき、隣接するセルへの書き込みを中断します。

    • 列挙型 OverOperation#Error の値のとき、書き込み処理をする前に、例外 AnnotationInvalidException をスローします。

_images/ArrayOption_overOperation.png

図 - 3.18.1 ArrayOption(overOperation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 書き込むデータ
String[] data = String[]{"や", "ま", "だ", " ", "た", "ろ", "う"};

// マッピングの定義
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledArrayCells(columnName="ふりがな", type=LabelledCellType.Right, size=6)
    @XlsArrayOption(overOperation=OverOperation.Error)
    private String[] nameRuby;
}

3.18.2. 書き込み時に配列・リストのサイズが余っている場合(remainedOperation)

アノテーション @XlsArrayOption を指定することで、書き込み時のセルの制御を指定することができます。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトの配列・リストのサイズに対して、属性 size の値が大きく、余っている場合の操作を指定します。

    • デフォルト値である列挙型 RemainedOperation#None の値のとき、隣接するセルへの書き込み、その後何もしません。

    • 列挙型 RemainedOperation#Clear の値のとき、隣接するセルへの書き込み、その後、余っているセルの値をクリアします。

_images/ArrayOption_remainedOperation.png

図 - 3.18.2 ArrayOption(remainedOperation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 書き込むデータ
String[] data = String[]{"あ", "べ", " ", "あ", "い"};

// マッピングの定義
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsLabelledArrayCells(columnName="ふりがな", type=LabelledCellType.Right, size=6)
    @XlsArrayOption(remainedOperation=RemainedOperation.Clear)
    private String[] nameRuby;
}

3.19. @XlsRecordOption

アノテーション @XlsHorizontalRecords@XlsVerticalRecords において、書き込み時のレコードの操作を指定するためのアノテーションです。 [ver.2.0+]

3.19.1. 書き込み時に配列・リストのサイズが不足している場合(overOperation)

アノテーション @XlsRecordOption を指定することで、書き込み時のレコードの制御を指定することができます。

  • 属性 overOperation で、書き込み時にJavaオブジェクトのレコード数に対して、シートのレコード数が足りない場合の操作を指定します。

    • デフォルト値である列挙型 OverOperation#Break のとき、レコードの書き込みを中断します。

    • 列挙型 OverOperation#Copy のとき、指定すると上部のセルの書式を下部にコピーして値を設定します。

      • @XlsVerticalRecords のときは、左側のセルを右側にコピーして値を設定します。

    • 列挙型 OverOperation#Insert のとき、行を挿入してレコードを書き込みます。その際に、上部のセルの書式をコピーします。

      • @XlsVerticalRecords のときは、サポートしていません。

_images/RecordOption_overOperation.png

図 - 3.19.1 RecordOption(overOperation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 書き込むデータ
List<UserRecord> data = new ArrayList<>();
data.add(new UserRecord(1, "山田 太郎"));
data.add(new UserRecord(2, "山田 花子"));
data.add(new UserRecord(3, "鈴木 一郎"));

// マッピングの定義
@XlsSheet(name="Users")
public class SheetObject {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    @XlsRecordOption(overOperation=OverOperation.Insert)
    private List<UserRecord> records;

}

3.19.2. 書き込み時に配列・リストのサイズが余っている場合(remainedOperation)

アノテーション @XlsRecordOption を指定することで、書き込み時のセルの制御を指定することができます。

  • 属性 remainedOperation で、書き込み時にJavaオブジェクトのレコード数に対して、シートのレコード数が余っている場合の操作を指定します。

    • デフォルト値である列挙型 RemainedOperation#None の値のとき、レコードを書き込み、その後何もしません。

    • 列挙型 RemainedOperation#Clear の値のとき、レコードを書き込み、その後、余っているセルの値をクリアします。

    • 列挙型 RemainedOperation#Delete の値のとき、レコードを書き込み、その後、余っている行を削除します。

      • @XlsVerticalRecords のときは、サポートしていません。

_images/RecordOption_remainedOperation.png

図 - 3.19.2 RecordOption(remainedOperation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 書き込むデータ
List<UserRecord> data = new ArrayList<>();
data.add(new UserRecord(1, "山田 太郎"));
data.add(new UserRecord(2, "山田 花子"));

// マッピングの定義
@XlsSheet(name="Users")
public class SheetObject {

    @XlsHorizontalRecords(tableLabel="ユーザ一覧")
    @XlsRecordOption(remainedOperation=RemainedOperation.Clear)
    private List<UserRecord> records;

}

3.20. @XlsRecordFinder

アノテーション @XlsHorizontalRecords@XlsVerticalRecords において、データレコードの開始位置が既存のアノテーションの属性だと表現できない場合に、任意の実装方法を指定するようにします。 [ver.2.0+]

  • 属性 value で、レコードの開始位置を検索する RecordFinder の実装クラスを指定します。

  • 属性 args で、レコードの開始位置を検索する実装クラスに渡す引数を指定します。

_images/RecordFinder.png

図 - 3.20.1 RecordFinder

コード - 3.20.1 任意の位置のレコードをマッピングする場合
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// マッピングの定義
@XlsSheet(name="Users")
public class SheetSheet {

    // クラスAに対するマッピング定義
    @XlsOrder(1)
    // マッピングの終了条件が、「クラスB」であるため、terminalLabelを指定します。汎用的に正規表現で指定します。
    @XlsHorizontalRecords(tableLabel="成績一覧", bottom=2, terminal=RecordTerminal.Border, terminateLabel="/クラス.+/")
    // クラスAの見出しを探すために、属性argsでクラス名を指定します。
    @XlsRecordFinder(value=ClassNameRecordFinder.class, args="クラスA")
    private List<Record> classA;

    // クラスBに対するマッピング定義
    @XlsOrder(2)
    // マッピングの終了条件が、終端のセルに罫線があるのため、terminalを指定します。
    @XlsHorizontalRecords(tableLabel="成績一覧", bottom=2, terminal=RecordTerminal.Border, terminateLabel="/クラス.+/")
    // クラスAの見出しを探すために、属性argsでクラス名を指定します。
    @XlsRecordFinder(value=ClassNameRecordFinder.class, args="クラスB")
    private List<Record> classB;

}

// クラス用の見出しのレコードを探すクラス
class ClassNameRecordFinder implements RecordFinder {

    @Override
    public CellPosition find(ProcessCase processCase, String[] args, Sheet sheet,
            CellPosition initAddress, Object beanObj, Configuration config) {

        // アノテーション @XlsRecordFinder の属性argsで指定された値を元にセルを検索します。
        final String className = args[0];
        Cell classNameCell = CellFinder.query(sheet, className, config)
                .startPosition(initAddress)
                .findWhenNotFoundException();

        // 見出し用のセルから1つ下がデータレコードの開始位置
        return CellPosition.of(classNameCell.getRowIndex()+1, initAddress.getColumn());
    }

}

// ユーザレコードの定義
public class UserRecord {

    @XlsColumn(columnName="No.", optional=true)
    private int no;

    @XlsColumn(columnName="氏名")
    private String name;

    @XlsColumn(columnName="算数")
    private Integer sansu;

    @XlsColumn(columnName="国語")
    private Integer kokugo;

    @XlsColumn(columnName="合計")
    @XlsFormula(value="SUM(D{rowNumber}:E{rowNumber})", primary=true)
    private Integer sum;

    @XlsIgnorable
    public boolean isEmpty() {
        return IsEmptyBuilder.reflectionIsEmpty(this, "positions", "labels", "no");
    }

    // getter、setterは省略

}

3.21. @XlsCommentOption

アノテーション @XlsComment@XlsLabelledComment において、書き込み時のコメントの書式などを指定するためのアノテーションです。 [ver.2.1+]

また、フィールド Map<String, String> comments でコメントをマッピングするセルに対しても指定可能です。

3.21.1. 書き込み時にコメントの表示/非表示設定をする。

アノテーション @XlsCommentOption の属性 visible を指定することで、書き込み時にコメントの表示/非表示設定をすることができます。

  • true場合、表示する設定になり、常にコメントが表示されます。

  • 既に設定されているコメントの設定よりも、アノテーションの設定が優先されます。

_images/CommentOption_visible.png

図 - 3.21.1 CommentOption(visible)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 書き込むデータ
SampleSheet sheet = new SampleSheet();
sheet.birthdayDescription = "yyyy/MM/dd の形式で設定してください。";
sheet.setBirthdayComment("設定してください。");

// マッピングの定義
@XlsSheet(name="Users")
public class SampleSheet {

    private Map<String, String> comments;

    @XlsCommentOption(visible=false)
    @XlsLablledComment(label="誕生日")
    private String birthdayDescription;

    @XlsCommentOption(visible=true)
    @XlsLabelledCell(label="誕生日", type=LabelledCellType.Right)
    private LocalDate birthday;

    // 誕生日の値セルのコメントの設定
    public void setBirthdayComment(String comment) {
        if(comments == null) {
            this.comments = new HashMap<>();
        }
        this.comments.put("birthday", comment);
    }

}

3.21.2. 書き込み時にコメントの枠サイズを指定する

アノテーション @XlsCommentOption の属性 verticalSize / horizontalSize を指定することで、書き込み時のコメントの枠サイズを指定することができます。

  • 属性 verticalSize にて、コメント枠の縦サイズを指定します。

    • 単位は行数です。

    • 既にコメントが設定されている場合は、この設定は無視されます。

  • 属性 horizontalSize にて、コメント枠の横サイズを指定します。

    • 単位は列数です。

    • 既にコメントが設定されている場合は、この設定は無視されます。

_images/CommentOption_size.png

図 - 3.21.2 CommentOption(verticalSize/horizontalSize)

1
2
3
4
5
6
7
8
9
// マッピングの定義
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsCommentOption(verticalSize=5, horizontalSize=2)
    @XlsCellComment(address="B2")
    private String value;

}

3.21.3. 書き込み時に既存のコメントを自動削除する

アノテーション @XlsCommentOption の属性 removeIfEmpty を指定することで、書き込むコメントの値が空のとき、既存のコメントを自動削除することができます。

属性 removeIfEmpty の初期値は false で削除されません。

1
2
3
4
5
6
7
8
9
// マッピングの定義
@XlsSheet(name="Users")
public class SampleSheet {

    @XlsCommentOption(removeIfEmpty=true)
    @XlsCellComment(address="B2")
    private String value;

}

3.21.4. コメントの読み込み、書き込みの処理の実装を切り替える

アノテーション @XlsCommentOption の属性 handler にて、コメントの処理を独自実装に切り替えることができます。

標準のコメントの処理は、 CellCommentHandler の実装クラス DefaultCellCommentHandler であるため、通常はこのクラスを継承してカスタマイズします。

全体の処理を切り替えたい場合は、システム設定CellCommentOperator のプロパティ commentHandler を変更します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@XlsSheet(name = "独自実装")
public class CustomHandlerSheet {

    private Map<String, CellPosition> positions;

    private Map<String, String> labels;

    private Map<String, String> comments;

    @XlsSheetName
    private String sheetName;

    @XlsLabelledCell(label = "標準の処理", type = LabelledCellType.Right)
    private String value1;

    @XlsCommentOption(handler = CustomCellCommentHandler.class)
    @XlsLabelledCell(label = "独自実装の処理", type = LabelledCellType.Right)
    private String value2;

}

/**
 * カスタマイズしたセルのコメント処理
 */
public class CustomCellCommentHandler extends DefaultCellCommentHandler {

    public CustomCellCommentHandler() {
        super();
        // 初期設定値の変更
        setMaxHorizontalSize(5);
        setMaxVerticalSize(4);
    }

    // 読み込み時の処理
    @Override
    public Optional<String> handleLoad(final Cell cell, Optional<XlsCommentOption> commentOption) {

        Optional<String> comment = super.handleLoad(cell, commentOption);

        // 改行を除去する。
        return comment.map(text -> text.replaceAll("\r|\n|\r\n", ""));
    }

    // 書き込み時の処理
    @Override
    public void handleSave(final Cell cell, final Optional<String> text, final Optional<XlsCommentOption> commentOption) {

        // 改行を除去する。
        text.map(comment -> comment.replaceAll("\r|\n|\r\n", ""))
                .ifPresent(comment -> super.handleSave(cell, Optional.of(comment), commentOption));

    }

}

3.21.5. 書き込み時にコメントの枠サイズを自動設定する

アノテーション @XlsCommentOption の属性 verticalSize / horizontalSize でコメント枠のサイズを指定しない場合は、 書き込むコメントの文字数、改行数によって自動的に設定されます。

ただし、コメント枠のサイズは、行数、列数で指定するため、コメントが表示される領域のセルのサイズが他と異なる場合、 意図したサイズにならない場合があります。

その際は、アノテーション @XlsCommentOption を使用してサイズを直接指定します。

または、標準の自動設定値を変更します。 ここでは、標準の設定値を変更します。

標準のコメントの処理は、 CellCommentHandler の実装クラス DefaultCellCommentHandler で指定されます。 この実装は、システム設定CellCommentOperator のプロパティ commentHandler で保持しています。

初期値では、コメントの縦サイズは最大4行分まで、横サイズは最大3列分となります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// コメントを処理するハンドラのインスタンスを生成します。
DefaultCellCommentHandler commentHandler = new DefaultCellCommentHandler();

// コメントの縦サイズの最大サイズを指定します。
commentHandler.setMaxVerticalSize(5);

// コメントの横サイズの最大サイズを指定します。
commentHandler.setMaxHorizontalSize(5);

// システム設定値を変更します。
XlsMapper xlsMapper = new XlsMapper();
xlsMapper.getConfiguration().getCommentOperator().setCommentHandler(commentHandler);