38
47

More than 5 years have passed since last update.

Jxls 使い方メモ

Last updated at Posted at 2016-03-20

Jxls とは

Excel 帳票を生成するための Java ライブラリ。

Excel ファイルをテンプレートファイルとして使用できる。

Hello World

インストール

build.gradle
dependencies {
    compile 'org.jxls:jxls-poi:1.0.11'
}

Excel テンプレート

jxls.jpg

実装

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);
        }
    }
}

実行結果

jxls.jpg

説明

依存関係

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 POIJava 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 クラスを使って任意の値を渡すことができる。

式言語

実験

jxls.jpg

↓ 出力結果

jxls.jpg

まとめ

  • 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:繰り返し

基本

テンプレート

jxls.jpg

実装

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);
        }
    }
}

実行結果

jxls.jpg

  • 繰り返しには、 Each-Command を使用する。
  • 記法は、 jx:each(<options>)
  • <options> には、以下の値を設定している。
    • items
      • 繰り返し処理対象のコレクション。
    • lastCell
      • 繰り返しの範囲を指定するためのセル座標。
      • コメントのあるセルから、ここで指定したセルまでの領域が繰り返し処理される。
  • Each-Command を実行する範囲が XLS Area に含まれるように jx:area で範囲を指定する必要がある。
    • もし jx:area(lastCell="D1") のように XLS Area が繰り返し範囲に収まっていないと、繰り返し処理は動作しなくなる。

繰り返しの範囲を限定する

テンプレート
jxls.jpg

実行結果
jxls.jpg

  • XLS Area に収まっていれば、 lastCelljx:area と一致させる必要はない

繰り返し中の各要素を取得する

テンプレート
jxl.jpg

出力結果
jxls.jpg

  • var で繰り返し中の各要素を格納する一時変数名を定義できる。

繰り返しの向きを指定する

テンプレート
jxls.jpg

出力結果
jxls.jpg

  • directrion で、繰り返しの向きを指定できる。
  • RIGHT で右方向に繰り返す。
  • デフォルトは DOWN で下方向に繰り返す。

結合されたセルを繰り返し処理

テンプレート
jxls.jpg

出力結果
jxls.jpg

  • 繰り返ししようとしているセルが結合されている場合は、「セルが結合されていない場合の位置」を lastCell に指定すると、うまく繰り返しできる。
    • lastCell="C1" とかにしちゃうと、残念なことになる。

If-Command:条件による表示の切り替え

基本

テンプレート
jxls.jpg

実行結果
jxls.jpg

  • 条件分岐には If-Command を使用する。
  • condition で条件式を指定する。
    • Java 側で Context に設定した値を参照することもできる。

条件が満たされたときの領域と満たされなかったときの領域を指定する

テンプレート
jxls.jpg

実行結果
jxls.jpg

  • areas で、条件が真のときと偽のときで、それぞれの参照先の領域を指定できる。
  • 指定は配列形式で、 ["真のときの領域", "偽のときの領域"] という形で指定する。

Grid-Command:表を生成する

基本

テンプレート
jxls.jpg

実装

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;
    }
}

実行結果
jxls.jpg

  • Grid-Command を使うと、表の出力をループなどを使わず少ない記述で実現できる。
  • 表を出力する部分に、 ${header}${cell} を記述する。
    • ${header} が、タイトルが出力される部分になり、
    • ${cell} が、各値が出力される部分になる。
  • jx:grid には以下の設定をする。
    • headers には、ヘッダーに出力するテキストをリストで渡す。
    • data には、データ領域に出力する情報をリストで渡す。
      • 各行をオブジェクトで設定する場合は、各行の値はプロパティとして取得できるようにしておかなければならない。
      • 単純にフィールドを public にしているだけだと、エラーになった。
    • props で、 data で指定したオブジェクトから各行の値を取得するときのプロパティ名を指定する。
      • 各プロパティの名前は、半角カンマで区切る。
    • areas で、処理対象となる範囲を指定する。
      • 配列形式で、 ["ヘッダー領域", "データ領域"] という形で指定する。

型ごとにセル書式を指定する

テンプレート
jxls.jpg

実行結果
jxls.jpg

  • formatCells で、型ごとにセル書式を指定できる。
  • 型名:参照するセル という形で指定する。
  • 「参照するセル」は、セル書式のコピー元となるセルの位置を指定する。
  • 複数の型に対して書式を指定したい場合は、半角カンマ区切りで列挙する。

Image-Command:画像を埋め込む

テンプレート
jxls.jpg

貼り付ける画像
image.png

実装

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);
        }
    }
}

実行結果
jxls.jpg

  • 画像を動的に貼り付けるには Image-Command を使用する。
  • jx:image には、以下のオプションを指定する。
    • src には、貼り付ける画像の byte 配列を渡す。
      • byte 配列は、 Jxls が提供している Util を使うと作成できる。
    • imageType には、画像の種類を渡す。
    • lastCell で指定したセルの左上まで、画像を伸縮させたものが貼り付けられる。

コマンドを自作する

テンプレート
jxls.jpg

実装

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);
        }
    }
}

実行結果
jxls.jpg

  • 詳細な説明は公式サイトにも載っていないので、デモにあった GroupRowCommand から推測して作成している。
  • 自作のコマンドは、 AbstractCommand クラスを継承して作成する。
  • addArea() メソッドをオーバーロードして、追加された Area を自作のコマンドクラスのインスタンス変数として保存しておく。
  • 任意のセッターメソッドを作成することで、パラメータとして値を渡せるようになる。
  • applyAt() メソッドないで、コマンドの処理を実装する。
    • Area から POI の API にアクセスするための Transformer が取得できる。
    • PoiTransformer.getWorkbook()Workbook オブジェクトが取得できる。
    • あとは、 POI の処理を実装する。
    • 最後に、コマンド処理を実行したあとの Area のサイズを返す。
    • 今回は特に大きさは変更していないので問題ないが、変更している場合は変更後の値を計算して返すようにする必要があるっぽい。
  • XlsCommentAreaBuilder.addCommandMapping() で自作のコマンドを登録できる。

参考

38
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
47