Jxls とは
Excel 帳票を生成するための Java ライブラリ。
Excel ファイルをテンプレートファイルとして使用できる。
Hello World
インストール
build.gradle
dependencies {
compile 'org.jxls:jxls-poi:1.0.11'
}
Excel テンプレート
実装
package sample.jxls;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = new FileInputStream(new File("./excel/template.xlsx"));
OutputStream out = new FileOutputStream(new File("./excel/out.xlsx"))) {
Context context = new Context();
context.putVar("message", "Hello Jxls!!");
JxlsHelper.getInstance().processTemplate(in, out, context);
}
}
}
実行結果
説明
依存関係
build.gradle
dependencies {
compile 'org.jxls:jxls:2.2.9'
compile 'org.jxls:jxls-poi:1.0.8'
}
- コア API と、具体的な Excel ライブラリとの連携を実装したモジュールを依存関係に追加する。
- Jxls のコア API は、具体的な Excel ライブラリには依存しない形になっている。
- Jxls は Apache POI と Java Excel API の2つのライブラリをサポートしている。
- ロギング API に Slf4j を使って、具体的な実装に logback を使う、みたいな感じ。
テンプレート
- テンプレートは Excel で作成する。
-
${...}
という形式の式言語で、変数の埋め込みなどを宣言できる。- Apache の JEXL という式言語用のライブラリを使っているらしい。
- 式言語による動的な埋め込みは、 XLS Area という特定の領域内でのみ有効になる。
- XLS Area を定義するための方法の一つとして、 Excel のセルコメントを使った方法が用意されている。
-
jx:area=(lastCell="A1")
は、現在のセルからA1
まで(つまりA1
セルのみ)を XLS Area にするという宣言になる。
実行
-
JxlsHelper
というクラスを使ってテンプレートから Excel 帳票の生成を実行する。 - テンプレートには、
Context
クラスを使って任意の値を渡すことができる。
式言語
実験
↓ 出力結果
まとめ
-
public
なフィールド、またはプロパティ(Getter
経由で取得できる値)のみ参照可能。 - 存在しないフィールドやメソッドを参照してもエラーにはならず、空文字扱いになる。
- プリミティブ型や
String
,Date
,BigInteger
,BigDecimal
型の値は、そのまま出力される。-
Date
型は、セルの書式を日付にしておけば、日付形式で表示される。 - (
null
を含む)それ以外の値は、セルに出力されない(罫線が消えてる。。。)
-
- オブジェクトの関連を
.
で連結させてたどることができる。 -
Map
のキーも、プロパティのように.
区切りで参照できる。
補足
Hoge.java
package sample.jxls;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class Hoge {
public String publicField = "Public Field";
protected String protectedField = "Protected Field";
String packageField = "Package Field";
private String privateField = "Private Field";
public String getPublicProperty() {
return "Public Property";
}
protected String getProtectedProperty() {
return "Protected Property";
}
String getPackageProperty() {
return "Package Property";
}
private String getPrivateProperty() {
return "Private Property";
}
public String getNull() {
return null;
}
public int getInt() {
return 10;
}
public long getLong() {
return 100;
}
public float getFloat() {
return 12.3f;
}
public double getDouble() {
return 45.6d;
}
public boolean getBoolean() {
return true;
}
public BigInteger getBigInteger() {
return new BigInteger("1234");
}
public BigDecimal getBigDecimal() {
return new BigDecimal("345.67");
}
public String getString() {
return "String Value";
}
public String getEmptyString() {
return "";
}
public Date getDate() {
return new Date();
}
public LocalDate getLocalDate() {
return LocalDate.now();
}
public String[] getArray() {
return new String[] {"a", "b", "c"};
}
public List<String> getList() {
return Arrays.asList("A", "B", "C");
}
public Map<String, String> getMap() {
Map<String, String> map = new HashMap<>();
map.put("foo", "FOO");
return map;
}
public Optional<String> getOptional() {
return Optional.of("Optional Value");
}
public String method() {
return "Method Value";
}
public Fuga getFuga() {
return new Fuga();
}
}
Fuga.java
package sample.jxls;
public class Fuga {
public Piyo getPiyo() {
return new Piyo();
}
}
Piyo.java
package sample.jxls;
public class Piyo {
public int getId() {
return 999;
}
}
Each-Command:繰り返し
基本
テンプレート
実装
package sample.jxls;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = new FileInputStream(new File("./excel/template.xlsx"));
OutputStream out = new FileOutputStream(new File("./excel/out.xlsx"))) {
Context context = new Context();
context.putVar("list", Arrays.asList(1, 2, 3));
JxlsHelper.getInstance().processTemplate(in, out, context);
}
}
}
実行結果
- 繰り返しには、 Each-Command を使用する。
- 記法は、
jx:each(<options>)
-
<options>
には、以下の値を設定している。-
items
- 繰り返し処理対象のコレクション。
-
lastCell
- 繰り返しの範囲を指定するためのセル座標。
- コメントのあるセルから、ここで指定したセルまでの領域が繰り返し処理される。
-
- Each-Command を実行する範囲が XLS Area に含まれるように
jx:area
で範囲を指定する必要がある。- もし
jx:area(lastCell="D1")
のように XLS Area が繰り返し範囲に収まっていないと、繰り返し処理は動作しなくなる。
- もし
繰り返しの範囲を限定する
- XLS Area に収まっていれば、
lastCell
はjx:area
と一致させる必要はない
繰り返し中の各要素を取得する
-
var
で繰り返し中の各要素を格納する一時変数名を定義できる。
繰り返しの向きを指定する
-
directrion
で、繰り返しの向きを指定できる。 -
RIGHT
で右方向に繰り返す。 - デフォルトは
DOWN
で下方向に繰り返す。
結合されたセルを繰り返し処理
- 繰り返ししようとしているセルが結合されている場合は、「セルが結合されていない場合の位置」を
lastCell
に指定すると、うまく繰り返しできる。-
lastCell="C1"
とかにしちゃうと、残念なことになる。
-
If-Command:条件による表示の切り替え
基本
- 条件分岐には If-Command を使用する。
-
condition
で条件式を指定する。- Java 側で
Context
に設定した値を参照することもできる。
- Java 側で
条件が満たされたときの領域と満たされなかったときの領域を指定する
-
areas
で、条件が真のときと偽のときで、それぞれの参照先の領域を指定できる。 - 指定は配列形式で、
["真のときの領域", "偽のときの領域"]
という形で指定する。
Grid-Command:表を生成する
基本
実装
package sample.jxls;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = new FileInputStream(new File("./excel/template.xlsx"));
OutputStream out = new FileOutputStream(new File("./excel/out.xlsx"))) {
Context context = new Context();
context.putVar("gridHeaders", Arrays.asList("ほげ", "ふが", "ぴよ"));
context.putVar("gridData", Arrays.asList(
new Main("hoge1", "fuga1", "piyo1"),
new Main("hoge2", "fuga2", "piyo2"),
new Main("hoge3", "fuga3", "piyo3")
));
JxlsHelper.getInstance().processTemplate(in, out, context);
}
}
private String hoge;
private String fuga;
private String piyo;
public Main(String hoge, String fuga, String piyo) {
this.hoge = hoge;
this.fuga = fuga;
this.piyo = piyo;
}
public String getHoge() {
return hoge;
}
public String getFuga() {
return fuga;
}
public String getPiyo() {
return piyo;
}
}
- Grid-Command を使うと、表の出力をループなどを使わず少ない記述で実現できる。
- 表を出力する部分に、
${header}
と${cell}
を記述する。-
${header}
が、タイトルが出力される部分になり、 -
${cell}
が、各値が出力される部分になる。
-
-
jx:grid
には以下の設定をする。-
headers
には、ヘッダーに出力するテキストをリストで渡す。 -
data
には、データ領域に出力する情報をリストで渡す。- 各行をオブジェクトで設定する場合は、各行の値はプロパティとして取得できるようにしておかなければならない。
- 単純にフィールドを
public
にしているだけだと、エラーになった。
-
props
で、data
で指定したオブジェクトから各行の値を取得するときのプロパティ名を指定する。- 各プロパティの名前は、半角カンマで区切る。
-
areas
で、処理対象となる範囲を指定する。- 配列形式で、
["ヘッダー領域", "データ領域"]
という形で指定する。
- 配列形式で、
-
型ごとにセル書式を指定する
-
formatCells
で、型ごとにセル書式を指定できる。 -
型名:参照するセル
という形で指定する。 - 「参照するセル」は、セル書式のコピー元となるセルの位置を指定する。
- 複数の型に対して書式を指定したい場合は、半角カンマ区切りで列挙する。
Image-Command:画像を埋め込む
実装
package sample.jxls;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
import org.jxls.util.Util;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = new FileInputStream(new File("./excel/template.xlsx"));
OutputStream out = new FileOutputStream(new File("./excel/out.xlsx"));
InputStream image = new FileInputStream(new File("./img/image.png"))) {
byte[] imageBytes = Util.toByteArray(image);
Context context = new Context();
context.putVar("myImage", imageBytes);
JxlsHelper.getInstance().processTemplate(in, out, context);
}
}
}
- 画像を動的に貼り付けるには Image-Command を使用する。
-
jx:image
には、以下のオプションを指定する。-
src
には、貼り付ける画像のbyte
配列を渡す。-
byte
配列は、 Jxls が提供しているUtil
を使うと作成できる。
-
-
imageType
には、画像の種類を渡す。 -
lastCell
で指定したセルの左上まで、画像を伸縮させたものが貼り付けられる。
-
コマンドを自作する
実装
MyCommand.java
package sample.jxls;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.jxls.area.Area;
import org.jxls.command.AbstractCommand;
import org.jxls.command.Command;
import org.jxls.common.CellRef;
import org.jxls.common.Context;
import org.jxls.common.Size;
import org.jxls.transform.poi.PoiTransformer;
public class MyCommand extends AbstractCommand {
private Area area;
private String value;
@Override
public String getName() {
return "myCommand";
}
@Override
public Size applyAt(CellRef cellRef, Context context) {
PoiTransformer transformer = (PoiTransformer)area.getTransformer();
Workbook wb = transformer.getWorkbook();
Sheet sheet = wb.getSheet(cellRef.getSheetName());
int rownum = cellRef.getRow();
int colnum = cellRef.getCol();
Row row = sheet.getRow(rownum);
Cell cell = row.getCell(colnum);
cell.setCellValue("<<" + this.value + ">>");
return this.area.getSize();
}
@Override
public Command addArea(Area area) {
super.addArea(area);
this.area = area;
return this;
}
public void setValue(String value) {
this.value = value;
}
}
Main.java
package sample.jxls;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.jxls.builder.xls.XlsCommentAreaBuilder;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = new FileInputStream(new File("./excel/template.xlsx"));
OutputStream out = new FileOutputStream(new File("./excel/out.xlsx"))) {
XlsCommentAreaBuilder.addCommandMapping("myCommand", MyCommand.class);
Context context = new Context();
JxlsHelper.getInstance().processTemplate(in, out, context);
}
}
}
- 詳細な説明は公式サイトにも載っていないので、デモにあった GroupRowCommand から推測して作成している。
- 自作のコマンドは、
AbstractCommand
クラスを継承して作成する。 -
addArea()
メソッドをオーバーロードして、追加されたArea
を自作のコマンドクラスのインスタンス変数として保存しておく。 - 任意のセッターメソッドを作成することで、パラメータとして値を渡せるようになる。
-
applyAt()
メソッドないで、コマンドの処理を実装する。-
Area
から POI の API にアクセスするためのTransformer
が取得できる。 -
PoiTransformer.getWorkbook()
でWorkbook
オブジェクトが取得できる。 - あとは、 POI の処理を実装する。
- 最後に、コマンド処理を実行したあとの Area のサイズを返す。
- 今回は特に大きさは変更していないので問題ないが、変更している場合は変更後の値を計算して返すようにする必要があるっぽい。
-
-
XlsCommentAreaBuilder.addCommandMapping()
で自作のコマンドを登録できる。