Apache POIをそのまま使うのがしんどいので、いいものはないかと探していたところ、Jxlsがよさげでした。
何が良いかって、ツリー構造を持ったデータを縦横自在に出力できる点が素晴らしいですね。
できないことといえば、マクロつけたりだとかフィルタをかけたりとかですが、そういうのはエクセルのテンプレートに埋め込んでおけば困ることはないです。
探したところ日本語ドキュメントが少なそうだったので、翻訳してみました。
変なところあればご指摘いただけると幸いです。
本記事はこちらの翻訳記事です。2020/3/30版のドキュメントを翻訳しています。
イントロダクション
JXLSはExcel帳票を簡単に生成するための小規模なJava製ライブラリです。JXLSで出力する帳票のフォーマットやデータのレイアウトは、独自の構文をExcelテンプレートに書き込むことで定義します。
いわゆる帳票機能のようなJavaアプリケーションにとって、Excel生成は必要不可欠なものとなっています。
Javaにはオープンソース、商用ともに、非常に優れたExcelファイル出力用のライブラリが存在します。そのうちオープンソースのものでは、Apache POIやJava Excel APIなどが特筆すべきライブラリでしょう。
しかし、それらのライブラリは非常に低レベル(※プリミティブの意)であり、簡素な構成のExcelファイルを出力しようと思っても大量のJavaコードを記述しなければなりません。
通常、それらのライブラリを使うと、一つ一つのセルに対し、手作業で書式やデータを設定しなければなりません。そして帳票のレイアウトやデータの書式設定が複雑になるにつれ、ソースコードも複雑になり、デバッグや保守が困難になってゆきます。それに加え、全てのExcelの機能がサポートされているわけではないため、全てがAPI経由で扱えるわけではありません。(例えばマクロやグラフなどはできません。)そういったサポートされていない機能に関しては、自分の手でExcelテンプレートにオブジェクトを埋め込み、その上でAPIを使ってデータを書き込むといった対応方法がとられます。
一方でJxlsはより高次のレベルでExcel帳票の生成を実現します。Excel帳票をJXLSで生成するためにすべきことといえば、帳票のフォーマットとデータの書式設定をテンプレートに埋め込み、そのテンプレートに対してJXLSエンジンでデータを流し込むだけです。Excel帳票を生成するほとんどのケースにおいて、JXLSエンジンの宣言と適切な設定を書くこと以外のソースコードの記述は必要ありません。
特徴
- XMLとバイナリ形式のExcel帳票出力 (低レベルのJAVA-Excel変換APIに依存)
- Javaコレクションクラスを行または列に変換して出力
- 条件付き結果出力
- 帳票の定義用マークアップ言語
- 複数シートの出力
- Excel数式の埋め込み
- パラメータ付き数式
- グループ化(※まとまりごとのデータ出力。Excel機能のグループ化とは別)に対応
- セル結合に対応
- Excel生成に対応したエリアリスナー
- コマンド定義用のExcelコメントに書くマークアップ構文
- コマンド定義用のXML構文
- カスタムコマンドの定義
- メモリ消費を抑え早く出力処理を行えるStreamingに対応
- 選択したシートのみをStreamingで扱うような指定が可能
- テーブル接続に対応
はじめ方ガイド
まずは、EmployeeオブジェクトをExcel帳票に出力したいケースを考えてみましょう。Employeeクラスはこんな感じでしょう。
public class Employee {
private String name;
private Date birthDate;
private BigDecimal payment;
private BigDecimal bonus;
// ... constructors
// ... getters/setters
}
JXLSを使ってこのオブジェクトをExcelに出力するためには、以下の一連の流れを行う必要があります。
- プロジェクトにJXLSに必要なライブラリを追加する
- 専用の構文を使ってExcelテンプレートを作成する
- JXLS APIを使って用意されたテンプレートにEmployeeのデータを埋め込む処理を行う
次の章からはこれら1つ1つの工程を詳細に見ていきましょう。
自分のプロジェクトにJXLSに必要なライブラリを追加する
Mavenを使って必要なライブラリを設定ファイル内で指定するのが最も簡単なやり方でしょう。
JxlsのjarはMavenのCentralレポジトリで取得可能です。
Jxlsのコアモジュールは以下のライブラリに依存しています。
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>2.8.0</version>
</dependency>
他にも、Sourceforgeのサイトからダウンロードしてjarを使う方法もあります。
Jxlsのコアモジュールの他にも、JavaからExcelに変換する操作を実行する全ての機能のベースとなっているJxls transformerエンジンへの依存を追加する必要があります。
Transformerの章に説明があるように(メインコンセプトを参照)Jxlsのコアモジュールは他の特定のJava - excelライブラリには依存していません。しかし、最近ではJxlsはApache POIやJava Excel APIライブラリの2つを扱えるように、それぞれに対して独立したインターフェースを提供しています。.
Apache POIベースの変換器を使用するためには、以下の依存関係をpom.xmlに追加します。
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>2.8.0</version>
</dependency>
Java Excel APIをベースにした変換機を使用するには、以下の依存関係を追加します。
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-jexcel</artifactId>
<version>${jxlsJexcelVersion}</version>
</dependency>
Excelテンプレートを作成する
テンプレートはデータの出力方法を指定するため独自構文を使用したExcelファイルです。
Jxlsはビルトインのマークアップ処理機構を持ち、Excelテンプレートから解釈して制御用のコマンドを抽出することができます。
カスタムマークアップ処理機構は必要に応じて作ることができます。独自の構文を定義してExcelテンプレートに埋め込み、Jxlsのコマンド構造を作ることができます。
次ではビルトインの構文処理機構について説明します。
デフォルトでは、JxlsはApache JEXLに対応しています。Apache JEXLはExcelテンプレート内で使われる言語で、Javaのオブジェクトが持つメソッドやプロパティを参照するために使われます。あるキーのもと、Jxlsのコンテクスト内では、オブジェクトは使用可能となります。Employeeの名前をセルに出力するためには、セルに${employee.name}
と書けばよいのです。基本的にJxlsにもJexl的な表現である${
と }
が溢れかえっています。構文をみることで、Employeeオブジェクトがemployee
キーの裏に存在すると推察できます。
プロパティの表記方法は設定可能です。例えば、[[employee.name]]
をプロパティの表記方法として使用すると設定することもできます。詳しくは、Expression language を参照してください。
Employeeオブジェクトを出力するためのサンプルテンプレートは最終的には以下のようになります。
このテンプレートの4行目では先ほど説明したJEXL構文を使ってemployeeオブジェクトのプロパティを参照しています。
A1セルには「jx:area(lastCell="D4")」といったExcelコメントが含まれています。このコメントではJxlsの最上位のテンプレートエリアがA1:D4の範囲であることを定義しています。
A4セルのコメントでは、Jxls Eaxh-Commandを「jx:each(items="employees" var="employee" lastCell="D4")
」という内容で定義しています。このEach-Commandはオブジェクトの集合をemployees
をキーにして、それぞれ取り出し、個々のデータをemployee
キー(var 属性で定義している)に配置していきます。Each Commandの対象範囲はA4:D4であり、contextに新しいEmployeeオブジェクトが現れるたび複製されていきます。
この例ではXlsCommentAreaBuilderクラスをJxlsエリアの構築に使っています。このクラスを使えばJxlsコマンドをExcelのセルコメントに書き込むことで定義できます。もし、Javaのソースコードで定義するほうが好ましい場合はセルからコメントを除いたものをテンプレートとすれば大丈夫です。
Jxls API をテンプレートの処理に使う
こちらがJxlsAPIをExcelテンプレートの処理に使う例です。
...
logger.info("Running Object Collection demo");
List<Employee> employees = generateSampleEmployeeData();
try(InputStream is = ObjectCollectionDemo.class.getResourceAsStream("object_collection_template.xls")) {
try (OutputStream os = new FileOutputStream("target/object_collection_output.xls")) {
Context context = new Context();
context.putVar("employees", employees);
JxlsHelper.getInstance().processTemplate(is, os, context);
}
}
...
この例ではクラスパスからテンプレート(object_collection_template.xls)を指定して読み込んでいます。そして生成結果のExcelファイルはtarget/object_collection_output.xlsに書き込まれます。
メインの処理は以下の一行で表されます。
JxlsHelper.getInstance().processTemplate(is, os, context);
デフォルトではJxlsHelperはテンプレートシートにデータを上書きすることを想定しています。
しかし、別シートに新しくデータを書き込むことも、以下のメソッドで可能です。
JxlsHelper.getInstance().processTemplateAtCell(is, os, context, "Result!A1");
この例ではResult
シートのA1
セルにエリアが書き込まれます。
この例で最終的に生成される帳票はこちらからダウンロードできます。見た目は以下のようになります。
リファレンス
コンセプト概要
Jxlsは以下のコンセプトによって成り立っています。
- XlsArea
- Command
- Transformer
それぞれの特徴について詳しく見ていきましょう。
Xlsエリア
Xlsエリアはエクセルファイル内に描かれる長方形のエリアのことを指し、具体的にはセルの範囲で指定される場合と、始点のセルと大きさ(行数と列数)を指定することで指定される場合があります。すなわちXlsエリアは指定された範囲すべてのセルを包含します。
それぞれのXlsAエリアには、Jxlsエンジンがエリアを処理する際に実行される一連のコマンドを持たせることが可能です。そして、Xlsエリアには子エリアをネストして持つことができます。そしてそれぞれの子エリアもまた親と同様に、コマンドと孫エリアを持つことができます。
Xlsエリアは以下の3つの方法から定義することができます。
- エクセルテンプレートに専用の構文で記載する方法:JxlsはデフォルトでXlsCommandAreaBuilderに対応した構文を用意しています。必要に応じてカスタム定義の構文を用意することもできます。
- XMLの定義ファイルを使用する方法:JxlsではXML構文解釈用としてXmlAreaBuilderクラスを用意しています。
- Jxls Java APIを利用する方法
Xlsエリアの定義についてのサンプルはこちらです。
Command
コマンドは1つ、または複数のXlsエリアに対する変換アクションのことを指します。
こちらに対応するJavaのインターフェースは以下のようになります。
public interface Command {
String getName();
List<Area> getAreaList();
Command addArea(Area area);
Size applyAt(CellRef cellRef, Context context);
void reset();
}
コマンドのメインメソッドはSize applyAt(CellRef cellRef, Context context)
です。
このメソッドではコマンドアクションをcellRef
変数で渡されたセルに対して、context
変数で渡されたデータを渡します。Contextはマップのように扱うことができ、データをコマンドに渡すために使われます。
Jxlsでは以下のビルトインコマンドを用意しています。
- Each-Command :リスト形式のデータに対し繰り返し処理をしている間実行されるコマンド
- If-Command :特定の条件下で処理を実行するコマンド
- Image-Command :画像をレンダリングするコマンド
- MergeCells-Command - セル結合
また、自分でカスタマイズしたコマンドを簡単に定義することができます。
データをコマンドに渡す処理はContextオブジェクトにより実現します。ContextはMapのように機能します。XLSテンプレートからはキー情報をもとに参照され、紐づく値がデータとしてセットされます。
Tramsformer
Transformerインターフェースにより、Xlsエリアは、特定のフレームワークに依存することなくExcelを扱うことができます。すなわち、異なるJava-to-Excelフレームワークとの変換インターフェースを使うことができます。
そのインターフェースは以下のような形で使われます。
public interface Transformer {
void transform(CellRef srcCellRef, CellRef targetCellRef, Context context, boolean updateRowHeight);
void setFormula(CellRef cellRef, String formulaString);
Set<CellData> getFormulaCells();
CellData getCellData(CellRef cellRef);
List<CellRef> getTargetCellRef(CellRef cellRef);
void resetTargetCellRefs();
void resetArea(AreaRef areaRef);
void clearCell(CellRef cellRef);
List<CellData> getCommentedCells();
void addImage(AreaRef areaRef, byte[] imageBytes, ImageType imageType);
void write() throws IOException;
TransformationConfig getTransformationConfig();
void setTransformationConfig(TransformationConfig transformationConfig);
boolean deleteSheet(String sheetName);
void setHidden(String sheetName, boolean hidden);
void updateRowHeight(String srcSheetName, int srcRowNum, String targetSheetName, int targetRowNum);
パッと見ではたくさんのメソッドが登場しているように見えますが、大部分はすでに基底の抽象クラスAbstractTransformer
で実装されています。そして、必要であればそこから継承して新たにjava-to-excelの実装をすればよいのです。
現在、Jxlsでは以下の2つのTransformerを用意しています。
- PoiTransformer
- JexcelTransformer
PoiTransformerはかの有名なApache POIというExcelファイル出力ライブラリを扱っています。
なお、JexcelTransformerは、旧Java Excel APIライブラリをベースにしています。
POI Transformer
PoiTransformerは、Apache POIをベースにしたTransformerインターフェースの実装です。
これは jxls-poi モジュールからのビルトイン Excel トランスフォーマーです。
POI-HSSFとPOI-XSSF/SXSSFの両方のワークブックをサポートしており、POIワークブックから、あるいはテンプレート入力ストリームからストリーミングまたは非ストリーミングのトランスフォームインスタンスを作成することができる複数のコンストラクタを持っています。
さらに、セル変換中にソースセルの行の高さや列の幅を無視することができます。これは setIgnoreRowProps(boolean) メソッドと setIgnoreColumnProps(boolean) メソッドで実現されます。
XLS Area
Introduction
XLS領域はJxlsPlusの主要概念です。基本的には、変換が必要なExcelファイル内の長方形の領域を表します。各XLS領域は、それに関連付けられた変換コマンドのリストと、入れ子になった子領域のセットを持つことができます。各子領域もまた、それ自身のコマンドと入れ子になった領域のセットを持つXLS領域です。トップレベルの XLS 領域とは、親領域を持たない領域のことです(他のどの XLS 領域にも入れ子になっていません)。
XLS Areaを作る
XLS areaを定義する方法は以下の3つあります。
- Excelへのマークアップ
- XMLによる定義
- Java APIを利用
それぞれの方法を詳しく説明しましょう。
XLSエリアを構築するためのExcelマークアップ
エクセルテンプレート内で特殊なマークアップを使用して、XLS領域を構築することができます。マークアップは、領域の最初のセルのExcelコメントに配置する必要があります。マークアップは次のようになります。
jx:area(lastCell = "<AREA_LAST_CELL>")
ここで、<AREA_LAST_CELL>
は定義された領域の最後のセルです。
このマークアップは、マークアップコメントのあるセルから始まり、<AREA_LAST_CELL>
で終わるトップレベルの領域を定義します。
例を見るために、Output Object Collection サンプルのテンプレートを見てみましょう。
A1セルのコメントでは、領域は次のように定義されています。
jx:area(lastCell="D4")
だからここでは、A1:D4セル範囲をカバーする領域を持っています。
マークアップを解析し、XlsAreaオブジェクトを作成するには、以下のようにXlsCommentAreaBuilderクラスを使用する必要があります。
// getting input stream for our report template file from classpath
InputStream is = ObjectCollectionDemo.class.getResourceAsStream("object_collection_template.xls");
// creating POI Workbook
Workbook workbook = WorkbookFactory.create(is);
// creating JxlsPlus transformer for the workbook
PoiTransformer transformer = PoiTransformer.createTransformer(workbook);
// creating XlsCommentAreaBuilder instance
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
// using area builder to construct a list of processing areas
List<Area> xlsAreaList = areaBuilder.build();
// getting the main area from the list
Area xlsArea = xlsAreaList.get(0);
次の 2 行のコードではJxlsAreaの作成のメインの処理を実行します。
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
List<Area> xlsAreaList = areaBuilder.build();
まず、XlsCommentAreaBuilderをインスタンス化してAreaBuilderのインスタンスを構築します。次に、areaBuilder.build()
メソッドを呼び出して、テンプレートからAreaオブジェクトのリストを作成します。
トップレベルの領域のリストを取得したら、それらをExcelの変換に使用することができます。
XLS領域を構築するためのXML設定
XML マークアップで XLS 領域を定義したい場合は、次のような方法があります。
まず、領域を定義する XML 設定を作成する必要があります。
例として、XML Builder を使用してオブジェクト コレクションの出力から簡単な XML 設定を考えてみましょう。
<xls>
<area ref="Template!A1:D4">。
<each items="employees" var="employees" ref="Template!
<area ref="Template!A4:D4"/>
</each>
</area>
</xls>
ルート要素はxlsです。次に、各トップレベル領域を定義する領域要素をいくつかリストアップします。
ここでは、TemplateシートにA1:D4というトップレベルの領域が1つあります。
<area ref="Template!A1:D4">
この領域の中では、特定のコマンドのための特別な要素を使用して、関連するコマンドを定義します。この場合、各xml要素で各コマンドを定義しています。各コマンドに関連付けられた領域はref属性で示されています。
<each items="employees" var="employees" ref="template!A4:D4">
とします。
各コマンドの中には、入れ子になっている領域パラメータがあります。
<area ref="template!
JavaのAPIでXLS Areaを構築する
Java APIを使用してXLSエリアを作成するには、XlsAreaクラスのコンストラクタのいずれかを使用することができます。以下のコンストラクタが利用可能です。
public XlsArea(AreaRef areaRef, Transformer transformer);
public XlsArea(String areaRef, Transformer transformer);
public XlsArea(CellRef startCell, CellRef endCell, Transformer transformer);
public XlsArea(CellRef startCellRef, Size size, List<CommandData> commandDataList, Transformer transformer);
public XlsArea(CellRef startCellRef, Size size);
public XlsArea(CellRef startCellRef, Size size, Transformer transformer);
トップレベルの領域を構築するには、領域がトランスフォームできるようにTransformer
インスタンスを提供しなければなりません。
そして、セル範囲を文字列として使用するか、CellRef
セル参照オブジェクトを作成して領域のSize
を設定することで、領域のセルを定義しなければなりません。
以下は、入れ子になった XLS テンプレート領域のセットをコマンドで作成するコードの一部です。
// create Transformer instance
// ...
// Create a top level area
XlsArea xlsArea = new XlsArea("Template!A1:G15", transformer);
// Create 'department' are
XlsArea departmentArea = new XlsArea("Template!A2:G13", transformer);
// create 'EachCommand' to iterate through departments
EachCommand departmentEachCommand = new EachCommand("department", "departments", departmentArea);
// create an area for employee 'each' command
XlsArea employeeArea = new XlsArea("Template!A9:F9", transformer);
// create an area for 'if' command
XlsArea ifArea = new XlsArea("Template!A18:F18", transformer);
// create 'if' command with the specified areas
IfCommand ifCommand = new IfCommand("employee.payment <= 2000",
ifArea,
new XlsArea("Template!A9:F9", transformer));
// adding 'if' command instance to employee area
employeeArea.addCommand(new AreaRef("Template!A9:F9"), ifCommand);
// create employee 'each' command and add it to department area
Command employeeEachCommand = new EachCommand( "employee", "department.staff", employeeArea);
departmentArea.addCommand(new AreaRef("Template!A9:F9"), employeeEachCommand);
// add department 'each' command to top-level area
xlsArea.addCommand(new AreaRef("Template!A2:F12"), departmentEachCommand);
SimpleExporter
概要
SimpleExporterクラスを使用することで、1行のコードでオブジェクトのリストをExcelにエクスポートすることができます。
これは、GridCommandを含む特別な組み込みテンプレートを使用することで実現します。
使用方法
SimpleExporter インスタンスを作成し、その gridExport メソッドを実行するだけです。
new SimpleExporter().gridExport(headers, dataObjects, propertyNames, outputStream);
- headers - ヘッダのコレクション
- dataObjects - データオブジェクトのコレクション
- propertyNames - オブジェクトのプロパティをカンマで区切ったリスト.
- outputStream - 最終的なExcelを書き込むための出力ストリーム.
SimpleExporter の例を参照してください。
Custom Template
SimpleExporterが使用するテンプレートを登録するには、その registerGridTemplate メソッドを使用します。
public void registerGridTemplate(InputStream templateInputStream)
テンプレートにはGridCommandが定義されている必要があります。
その方法については、SimpleExporterの例を参照してください。
Transformerのサポート
GridCommandは現在POI Transformerでしかサポートされていないので、SimpleExporterで作業する場合はPOIを使用する必要があります。
Excel mark-up
Jxlsにおいて、エクセルに利用できるマークアップ構文には以下の3種類があります。
- Beanプロパティマークアップ
- エリア定義マークアップ
- コマンド定義マークアップ
Jxlsは、Excelのセルコメントからマークアップを読み取ることができるXlsCommentAreaBuilderクラスを提供しています。XlsCommentAreaBuilderは、一般的なAreaBuilderインターフェースを実装しています。
public interface AreaBuilder {
List<Area> build();
}
これは、エリアオブジェクトのリストを返す単一のメソッドを持つシンプルなインターフェイスです。
したがって、独自のマークアップを定義したい場合は、AreaBuilderの独自の実装を作成して、入力されたExcelテンプレート(または他の入力)を好きなように解釈することができます。
Beanプロパティマークアップ
jxls は Apache JEXL 式言語を使用して処理します。
将来のリリースでは、必要に応じてJEXLを他の式エンジンに置き換えることができるように、式言語エンジンが設定可能になる予定です。
JEXL式言語の構文については、ここで説明します。
Jxlsは、XLSテンプレートファイルの${ }
内にJEXL式を配置することを想定しています。
例えば、次のセル内容${department.chief.age} years
は、コンテクスト内にdepartmentオブジェクトがあると仮定して、JEXLを用いてdepartment.chief.ageを評価するように指示します。例えば、式 department.getChief().getAge()
が35と評価された場合、JxlsはXlsAreaの処理中に35 years
をセルに入れます。
XLSエリア定義マークアップ
Jxls領域マークアップは、Jxlsエンジンが処理するルートXlsAreaを定義するために使用されます。XlsCommentAreaBuilderは、Excelセルコマンドとして領域を定義するための以下の構文をサポートしています。
jx:area(lastCell="<LAST_CELL>")
ここでは、矩形領域の右下のセルを定義しています。最初のセルは、Excelのコメントが置かれたセルによって定義されます。
つまり、セルA1に次のコメントjx:area(lastCell="G12")があると仮定すると、ルート領域はA1:G12と読み込まれます。
テンプレートファイルからすべての領域を読み込むには、XlsCommentAreaBuilderを使用する必要があります。例えば、以下のコードスニペットは、すべての領域をxlsAreaListに読み込み、最初の領域をxlsArea変数に保存します。
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
List<Area> xlsAreaList = areaBuilder.build();
Area xlsArea = xlsAreaList.get(0);
ほとんどの場合、単一のルートXlsAreaを定義するだけで十分です。
コマンドのマークアップ
コマンドはXlsArea内で定義する必要があります。XlsCommentAreaBuilderは、Excelセルのコメントとして作成された以下のコマンド記法を受け付けます。
jx:<command_name>(attr1='val1' attr2='val2' ... attrN='valN' lastCell=<last_cell> areas=["<command_area1>", "<command_area2", ... "<command_areaN>"])
は、XlsCommentAreaBuilderに事前登録されている、または手動で登録されているコマンド名です。現在、以下のコマンド名が事前登録されています。
- each
- if
- image
カスタムコマンドは、XlsCommentAreaBuilderクラスのstatic void addCommandMapping(String commandName, Class clazz)
メソッドを使用して手動で登録することができます。
attr1, attr2,..., attrN
はコマンド固有の属性です。
例えば、If-Commandは条件式を設定するための条件属性を持っています。
<last_cell>
はコマンド本体領域の右下のセルを定義します。左上のセルは、コマンド表記が付けられているセルによって決まります。
<command_area1>, <command_area2>, ... <command_areaN>
- コマンドにパラメータとして渡すXLS領域です。
例えば、If-Commandは以下の領域を定義することを期待しています。
ifArea
は、If-command
条件が真と評価されたときに出力する領域への参照です。
elseArea
は、If-command
条件が false
と評価されたときに出力する領域への参照です(オプション)。
つまり、Ifコマンドのエリアを定義するには、そのエリア属性は次のようになります。
areas=["A8:F8","A13:F13"]
1つのセルコメントで複数のコマンドを定義することができます。例えば、「それぞれ」と「もしも」コマンドの定義は次のようになります。
jx:each(items="department.staff", var="employee", lastCell="F8")
jx:if(condition="employee.payment <= 2000", lastCell="F8", areas=["A8:F8","A13:F13"])
Expression Language
概要
デフォルトでは、Jxls は Apache JEXL 式言語を使用して、Excel テンプレートファイルで指定されたプロパティ式を評価します。
どのような式が使用できるかについては、JEXL構文リファレンスを参照してください。
Jexl の処理をカスタマイズする
Jexl処理をカスタマイズする必要がある場合は、TransformerからJexlEngineへの参照を取得し、必要な設定を適用することができます。
例えば、以下のコードは、カスタム Jexl 関数を demo 名前空間に登録します。
Transformer transformer = TransformerFactory.createTransformer(is, os);
...
JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) transformer.getTransformationConfig().getExpressionEvaluator();
Map<String, Object> functionMap = new HashMap<>();
functionMap.put("demo", new JexlCustomFunctionDemo());
evaluator.getJexlEngine().setFunctions(functionMap);
こんな感じでJexlCustomFunctionDemoにメソッドがあったとします。
public Integer mySum(Integer x, Integer y){
return x + y;
}
するとテンプレート上では以下のような関数が利用できます。
${demo:mySum(x,y)}
因みにここでの x と y は Context から渡されているパラメータです。
実装例については、JexlCustomFunctionDemo.javaを参照してください。
式エンジンの変更
Apache JEXL を使わず、他の式処理エンジンを使いたい場合もあるでしょう (例えば Spring Expression Language (SpEL) を使うなど)。
Jxlsでは、デフォルトの評価エンジンをお好みのもので代用することができます。
これを行うには ExpressionEvaluator インターフェースの一つのメソッドを実装して、式の評価処理をお好みのエンジンに委譲するだけです。
public interface ExpressionEvaluator {
Object evaluate(String expression, Map<String,Object> context);
}
そして、以下のコードに示すように、ExpressionEvaluator の実装を TransformationConfig に渡す必要があります。
ExpressionEvaluator evaluator = new MyCustomEvaluator(); // your own implementation based for example on SpEL
transformer.getTransformationConfig().setExpressionEvaluator(evaluator);
Each-Command
概要
Each-Commandは、コレクションを反復処理し、コマンドのXLS領域をクローンするために使用されます。これはJavaのfor
に類似した機能です。
コマンドの属性
各コマンドには以下の属性があります。
- varはJxlsコンテキスト内の変数名で、反復処理を行う際にコレクションから取り出した要素にアクセスするためのものです。
- items は、反復処理を行う対象のコレクション (Iterable>) または配列を含むコンテキスト変数の名前です。
- areaは各コマンド本体として使用されるXLS areaの参照範囲です。
- direction は Direction 列挙の値で、DOWN または RIGHT の値を持つことができ、コマンド本体の繰り返し方法(行単位または列単位)を示します。デフォルト値はDOWNです。
- select は、繰り返しの間にコレクションアイテムをフィルタリングするための式セレクタです。
- groupBy はグループ化を行うためのプロパティです。
- groupOrder は、グループの順序を示します ('desc' または 'asc' です)。
- orderBy には、カンマで区切られた名前と、ソート順のためのオプションの接頭辞 "ASC" (デフォルト) または "DESC" が含まれます。
- cellRefGeneratorは、ターゲットセル参照を作成するためのカスタムストラテジーです。
- multisheet は、コレクションを出力するシート名のリストを含むコンテキスト変数の名前です。
- lastCell はコマンドエリアの最後のセルを指すコマンドの共通属性です。
なお、var属性とitem属性は必須ですが、他の属性は省略することができます。
cellRefGeneratorとマルチシート属性の使用についての詳細は、複数のシートのセクションをチェックしてください。
コマンドの構築
他のJxlsコマンドと同様に、Java APIやExcelのマークアップ、XML設定を使用して各コマンドを定義することができます。
Java API 使い方
以下は、パッケージ org.jxls.examples にある Each-Command の作成例です。
// creating a transformer and departments collection
...
// creating department XlsArea
XlsArea departmentArea = new XlsArea("Template!A2:G13", transformer);
// creating Each Command to iterate departments collection and attach to it "departmentArea"
EachCommand departmentEachCommand = new EachCommand("department", "departments", departmentArea);
Excelマークアップの使い方
Excel マークアップで各コマンドを作成するには、コマンド本体領域の開始セルのコメントに特別な構文を使用する必要があります。
jx:each(items="employees" var="employee" lastCell="D4")
lastCell属性は、コマンドXlsArea本体の最後のセルを定義します。
XMLマークアップの使用方法
XML設定で各コマンドを作成するには、以下のマークアップを使用します。
<each items="employees" var="employee" ref="Template!A4:D4">
<area ref="Template!A4:D4"/>
</each>
ここで、ref属性は、各コマンドに関連付けられる領域を定義します。そして、内側の領域は、各コマンドの本体を定義します。通常は同じです。
ループの方向
デフォルトでは、各コマンドの方向属性はDOWNに設定されており、これはコマンド本体がExcel行の下方向に複製されることを意味します。
列ごとに領域を複製する必要がある場合は、方向属性をRIGHT値に設定する必要があります。
Java APIでは以下のようにします。
//... creating EachCommand to iterate departments
// setting its direction to RIGHT
departmentEachCommand.setDirection(EachCommand.Direction.RIGHT);
データのグループ化
各コマンドは、groupBy プロパティを使用してグループ化をサポートしています。groupOrder プロパティは順序を設定し、desc または asc を指定できます。groupOrder を指定せずに groupBy を記述した場合、ソートは行われません。
エクセルのマークアップでは、以下のようになります。
jx:each(items="employees" var="myGroup" groupBy="name" groupOrder="asc" lastCell="D6")
この例では、各グループは、Contextからアクセス可能な myGroup 変数を使用して参照することができます。
グループ化された要素には myGroup.item
を使用して参照することができます。つまり、従業員の名前を参照するには以下のように記述します。
${myGroup.item.name}
また、グループ内のすべての要素は、以下のようにグループのitems
プロパティからアクセスできます。
jx:each(items="myGroup.items" var="employee" lastCell="D6")
var属性を完全にスキップすることもでき、この場合、デフォルトのグループ変数名は_groupとなります。
他の例はこちらをご覧ください。
If-command
If-Commandは、コマンドの属性で指定した条件に応じてエリアを出力する条件付きコマンドです。
If-Commandの属性
If-commandには以下の属性があります。
-
condition
は条件分岐用の式を記述します。 -
ifArea
は条件式の評価結果がtrue
の場合に出力されるエリアのセル範囲です。 -
elseArea
は条件式の評価結果がfalse
の場合に出力されるエリアのセル範囲です。 -
lastCell
はコマンドが共通にもつ属性で、コマンドエリアの終端に位置するセルを指定します。
ifArea
とelseArea
は設定が任意の属性です。
Command生成
他のJxlsコマンドと同様に、Java APIやExcelのマークアップ、XML設定を使用してIf-Commandを定義することができます。
Java API 使用方法
以下は org.jxls.examples にある If-Command の生成例です。
// ...
// creating 'if' and 'else' areas
XlsArea ifArea = new XlsArea("Template!A18:F18", transformer);
XlsArea elseArea = new XlsArea("Template!A9:F9", transformer);
// creating 'if' command
IfCommand ifCommand = new IfCommand("employee.payment <= 2000", ifArea, elseArea);
Excelマークアップ
IfコマンドをExcelマークアップで生成するには、コマンド本体領域の開始セルのコメントで以下の構文を使用する必要があります。
jx:if(condition="employee.payment <= 2000", lastCell="F9", areas=["A9:F9","A18:F18"])
ここでのlastCell
はIfコマンドの適用範囲の最後のセルを示しています。
XMLマークアップ
XML設定でIf-Commandを生成するには、次のようなマークアップを使用します。
<area ref="Template!A9:F9">
<if condition="employee.payment <= 2000" ref="Template!A9:F9">
<area ref="Template!A18:F18"/>
<area ref="Template!A9:F9"/>
</if>
</area>
ここでのref
属性は、If-Commandに関連付けられるエリアを定義しています。