Apache POI 調査
Excelを操作したいみたいな状況は割とよくある。Pythonとかのスクリプト言語だと結構対処法をが見つかるけど、Scalaだと、Java資産のApache POIくらいしかない上に情報もそんなにない(poi.scala のようなラッパーライブラリもあるようだけど、プロダクションで使うには早計感ある)。Apache POIの使い方を調べて、人並みに使えるようになろうと思う。参考記載のJavaでのApache POIの使い方を元に、Scalaで書いて感じを掴む。
準備
必要なライブラリを build.sbt
に追記する。
libraryDependencies ++= Seq( "org.apache.poi" % "poi" % "5.0.0", "org.apache.poi" % "poi-ooxml" % "5.0.0" )
- https://mvnrepository.com/artifact/org.apache.poi/poi/5.0.0
- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml/5.0.0
Excelファイルの作成
build.sbt
の準備ができたら、Excelファイルを作成していく。
XLS ファイルの作成
今だと使う機会は少ないかもしれないが一応。XLSファイルを扱うのは、上記のライブラリのうち poi
の方だ。
package dev.tchiba.researchApachePoi import org.apache.poi.hssf.usermodel.HSSFWorkbook import java.io.FileOutputStream import scala.util.{Failure, Success, Try} object XLS { def main(args: Array[String]): Unit = { val fileName = "demo.xls" val workbook = new HSSFWorkbook() // XLSにはHSSFWorkbookを使う workbook.createSheet("Tab1") Try(new FileOutputStream(fileName)) match { case Failure(exception) => exception.printStackTrace() case Success(outputStream) => workbook.write(outputStream) println(s"An Excel file $fileName has been created") workbook.close() } } }
XLSX ファイルの作成
XLSXファイルを作成するには、build.sbt
記載のライブラリのうち、後者の poi-ooxml
が必要だ。処理はほとんど同じで、生成するインスタンスのクラスのみが異なる。
package dev.tchiba.researchApachePoi import org.apache.poi.xssf.usermodel.XSSFWorkbook import java.io.FileOutputStream import scala.util.{Failure, Success, Try} object XLSX { def main(args: Array[String]): Unit = { val fileName = "demo.xlsx" val workbook = new XSSFWorkbook() // XLSX を生成する場合は、XSSFWorkbookを使う workbook.createSheet("Tab1") Try(new FileOutputStream(fileName)) match { case Failure(exception) => exception.printStackTrace() case Success(outputStream) => workbook.write(outputStream) println(s"An Excel file $fileName has been created") workbook.close() } } }
Excelファイルにデータを書き込む
空のExcelファイルが作れても一ミリも嬉しくないので、データを書き込んでいこう。サンプルに下記のようなデータモデルを用意する。
case class Person( firstName: String, lastName: String, birthDay: LocalDate, email: String, phoneNumber: String, married: Boolean )
これをExcelに書き込むクラスを用意しよう。参考がJavaなので、あんまり Scala っぽくないけど、下手に外れると詰まるから諦める。
package dev.tchiba.researchApachePoi import models.Person import org.apache.poi.ss.usermodel.{Sheet, Workbook} import org.apache.poi.xssf.usermodel.{XSSFSheet, XSSFWorkbook} import java.io.FileOutputStream import scala.util.{Failure, Success, Try} class PersonExcelWriter { def write(fileName: String, personList: List[Person]): Unit = { val workbook = prepareWorkbook(personList) Try(new FileOutputStream(fileName)) match { case Failure(exception) => exception.printStackTrace() case Success(outputStream) => workbook.write(outputStream) println(s"An Excel file $fileName has been created") workbook.close() } } private def prepareWorkbook(personList: List[Person]): Workbook = { val workbook: XSSFWorkbook = new XSSFWorkbook() val personSheet: XSSFSheet = workbook.createSheet("Persons") prepareHeader(personSheet) prepareTable(personSheet, personList) workbook } private def prepareHeader(personSheet: Sheet): Unit = { // ヘッダー行を作成する val headerRow = personSheet.createRow(0) // セルを作成する val firstNameHeaderCell = headerRow.createCell(0) val lastNameHeaderCell = headerRow.createCell(1) val birthdayHeaderCell = headerRow.createCell(2) val emailHeaderCell = headerRow.createCell(3) val phoneNumberCell = headerRow.createCell(4) val marriedHeaderCell = headerRow.createCell(5) // セルに値をセットする firstNameHeaderCell.setCellValue("First name") lastNameHeaderCell.setCellValue("Last name") birthdayHeaderCell.setCellValue("Birthday") emailHeaderCell.setCellValue("Email") phoneNumberCell.setCellValue("Phone number") marriedHeaderCell.setCellValue("Married") } private def prepareTable(personSheet: Sheet, personList: List[Person]): Unit = { personList.zipWithIndex.foreach { case (person, index) => // 行を作成する val row = personSheet.createRow(index + 1) // セルを作成する val firstNameCell = row.createCell(0) val lastNameCell = row.createCell(1) val birthdayCell = row.createCell(2) val emailCell = row.createCell(3) val phoneNumberCell = row.createCell(4) val marriedCell = row.createCell(5) // セルに値をセットする firstNameCell.setCellValue(person.firstName) lastNameCell.setCellValue(person.lastName) birthdayCell.setCellValue(person.birthDay.toString) emailCell.setCellValue(person.email) phoneNumberCell.setCellValue(person.phoneNumber) marriedCell.setCellValue(person.married) } } }
手順としては
- ワークブックを用意する
- シートを用意する
- カラムにデータを埋めていく
- 最後に、ワークブックをファイルに保存する
となる。
これを使う処理を書こう。
package dev.tchiba.researchApachePoi import models.Person import java.time.LocalDate object Main { def main(args: Array[String]): Unit = { val person1 = Person( firstName = "太郎", lastName = "田中", birthDay = LocalDate.of(1990, 2, 2), email = "taro.tanaka@xxx.com", phoneNumber = "090XXXXXXX", married = true ) val person2 = Person( firstName = "花子", lastName = "佐藤", birthDay = LocalDate.of(1992, 9, 9), email = "hanako.sato@xxx.com", phoneNumber = "090YYYYYYY", married = false ) val personList = List(person1, person2) val writer = new PersonExcelWriter() writer.write("person.xlsx", personList) } }
実行して出力されたファイルを開いてみると、(私のPCにはExcel入っていないので、Numbersを利用)、想定通りに値が入っていることが分かる。
参考
Apache POI に関する体系的な情報が探しづらいなか、下記の本がKindle Unlimited にあったのが救いだった。