Excel方眼紙を支える技術

最近仕事でExcel方眼紙を読んだり書いたりするプログラムばかり書いているのですが、そんなExcel方眼紙を支える最新のJava技術を紹介したいと思います。

なにはなくともPOI

JavaExcelと言えば外せないのがPOIです。以前はJExcel APIという対抗馬もあったのですが、Excel 2007に対応していないので現在ではPOIが唯一の選択肢といっていいでしょう。

POIは非常にプリミティブなAPIを提供するライブラリで、使いこなすにはそれなりの修練が必要です。そのため、可能であれば実際の読み書きには後述するXLSBeansやjXLSなどを使った方がよいでしょう。ただし、jXLSのようなテンプレートエンジンでは細かいセルのスタイル設定などはできないため、POIを併用しないとならないケースも多いです。Excel方眼紙を支える技術のベースとして押さえておくべきライブラリといえるでしょう。

なお、Scalaからであればpoi4sというPOIのラッパーライブラリがあります。以下のようにScalaのコレクションAPIを使ってシートのスキャンができるという優れ物です。

// 列の見出しを検索
sheet.find(_.text == "Name").map { header =>
  // その列を一番下までスキャンしてセルの値を出力
  sheet.column(header.colNum)
    .filter(_.rowNum > header.rowNum).foreach { cell =>
      println(cell.text)
    }
}

Excelファイルの読み込みならXLSBeans

Excelファイルの読み込みに一押しなのがXLSBeansです。これはアノテーションExcelをJavaBeanにマッピングするというもので、セルの物理的な座標ではなく、テキストなどを指定してマッピングを行うことができるので、多少の行追加や列追加などはものともしないという特徴があります。

@Sheet(name="Users")
public class UserList {

  @LabelledCell(label="Title", type=LabelledCellType.Right)
  public String title;

  @HorizontalRecords(tableLabel="User list", recordClass=User.class)
  public List<User> users;

  public static class User {

    @Column(columnName="ID")
    public int id;

    @Column(columnName="Name")
    public String name;

    @Column(columnName="Gender", merged=true)
    public String gender;

  }
}

上記は非常にシンプルな例ですが、実際には読み込む位置の微調整など柔軟なマッピング設定に加えてマッピング後の後処理も可能なので、何も考えずにフリーダムに作成したExcelファイルでも工夫次第でマッピングすることができます。マージされたセルも空気を読んでいい感じに扱ってくれます。もはやXLSBeansでマッピングできないExcelファイルはないと言っても過言ではないでしょう。

読み込んだJavaBeanをBean Validationでバリデーションしたり、JSONXMLに変換したり、DBに登録したりなど、他のJava技術との相性もよいです。

Excel帳票出力の本命ははjXLSかJETTか?

Excel帳票の出力にはjXLSJETTといったExcelテンプレートエンジンが便利です。これらはテンプレートとなるExcelファイル内にVelocityなどのようなディレクティブを埋め込んでおくことで変数で置換したり、繰り返し出力などを行うことができるというものです。

国内ではSeasarプロジェクトで開発されていたFisshplateという同種のExcelテンプレートエンジンがありますが、こちらは繰り返し部分にあわせて数式の範囲を追従させることができないため、Excel側の数式で集計処理を行う必要があるケースには対応できません。おすすめはjXLSですが、最近はjXLSよりも高機能なJETTというものもあります。JETTのサイトにjXLSとの機能比較が掲載されているので参考にしていただければと思います。以下では筆者が使い込んでいるjXLSについて書きます。

jXLSは非常に便利なプロダクトですが、欠点もあります。たとえば繰り返し部分に条件付き書式などを設定しておいても着いてきてくれなかったり、セルのマージ、色や罫線といったスタイルの設定などはできません。こういうものについてはテンプレートを工夫するか、あとからPOIを使ってなんとかする必要があります。

ただ、これらの問題以上に最もつらいのは、繰り返し部分に追従させたい数式は独自の記法で記述しておかないといけないということです。数式の数が多いと独自記法に書き換えるだけでも大変なのですが、真のつらさはテンプレートの修正時にやってきます。独自記法に置換した数式は通常の数式と違ってあとから行や列を挿入した場合にセル位置が着いてきてくれないので、手動で修正する必要があります。数式の数が多いとテンプレートに1列追加するだけで破滅的な被害をもたらす可能性があるので取り扱いには注意が必要です。

どうしてもダメな場合の最終兵器

XLSBeansやjXLSでできない処理についてはPOIを併用するわけですが、POIを持ってしても実現できない機能があるのも事実です(たとえばファイル間でのシートのコピーはPOIでは行うことはできません)。このような場合のリーサルウェポンがCOMです。ExcelがCOMとして提供しているあらゆる操作が可能になります。

JavaからCOMを使うためのライブラリにはJComJacobcom4jなどがあります。あまり多用するものではないので各ライブラリの使い勝手や品質については詳しくは触れませんが、JComやJacobは単純にCOMを叩くためのAPIを提供するライブラリですが、com4jはCOMオブジェクトに対応したJavaクラスを生成するのでタイプセーフにアクセスすることができるという特徴があります。COMを多用せざるを得ないケースではcom4jの利用を検討するとよいでしょう。

ただし、COMには

  • Windowsでしか使用することができない
  • 実行環境にExcelがインストールされている必要がある
  • エラーハンドリングが難しい場合がある

といったデメリットがあります。たとえば処理中にエラーなどでダイアログが表示されてしまうとJavaからはハンドリングする術がなくExcelのプロセスが残ったままになってしまうなど、完全なオートメーションにはそもそも向いていません。とはいえ、POIではどうにもならない場合の奥の手として覚えておいても損はないでしょう。

番外編、フロントエンドExcel方眼紙

Excelは表形式の明細を入力するのに非常に優秀なインターフェースを備えています。なによりユーザはExcelに慣れきっています。Webアプリでも通常の入力画面に加えて大量のデータを一括投入するためにExcelCSVファイルのアップロード機能が要求されるようなケースもあるでしょう。

でも、データ投入のインターフェースとしてExcelを使うのであれば、Webブラウザ上でExcelと同様のインターフェースを実現できればそれで済むのではないでしょうか?この手のJavaScriptライブラリには様々なものがありますが、個人的におすすめなのがHandsontableです。操作性はまさにExcelそのものでExcelとのコピー&ペーストも可能です。また、ヘッダにHTMLを埋め込むことができたり、既存のサーバサイドフレームワークと組み合わせやすいという点もポイントが高いです。

このようにフロントエンドでもExcel方眼紙を実現する素晴らしいHandsontableですが、問題はバグも結構多いということです。Githubを見るとわかりますが、累計で1000件以上のIssueが上げられています。もちろんバグではなく機能追加の要望も多いのですが、とにかく数が多すぎて捌ききれていない感じです。実際Excelによく似ているので期待値が高くなってしまい、ユーザさんからも「あんなことはできないの?」「こんなことはできないの?」と言われかねない諸刃の剣です。

とはいえ、使い勝手のよい明細形式の入力インターフェースをほとんど手間をかけずに実装できるという意味でHandsontableのようなライブラリは非常に有用です。うまく活用すると大きな効果を発揮することでしょう。

まとめ

以上、Excel方眼紙を支える技術について紹介しました。

もちろん敢えて書いているのですが、ここではExcel方眼紙と書いていますが、これらは世間一般で言われるいわゆるExcel方眼紙ではなく、ちゃんとExcelに適した使い方をしているもの(主に表形式でデータを記載しているもの)を扱うための技術です。Excelというととかく嘲笑の対象になりがちですが、目的さえ間違わなければ非常に有用なソフトウェアだと思うのでうまく活用していきたいものです。