【Java】Apache TikaでExcel（に限らず色々な）ファイルをパースする【Kotlin】

# やりたいこと
xlsxファイルをパースします。
今回はApache Tikaを用いてパースを行います。

## パースする内容
今回は以下のような内容のファイルをパースしていきます。

補足として、今回の記事を書くにあたって確認用に利用したファイルはGoogle Docsのスプレッドシートで生成したものなので、純正/サードパーティ性のソフトから生成した場合もちゃんと動くのか確認できていません。
また、実際のファイルではフォントをバラバラに変更したりしていましたがパース結果に影響は有りませんでした。

|シート1|||
|---|---|---|
|hoge|ふが|ピヨ|
|fizz|バッズ|fizzBuzz|

# やりかた
ライブラリを導入してparseすればできます。
今回は`xml`系（含xlsx）ファイルのみに対応してパースする方法と、Tikaにファイル種別を検出してもらって自動でパースする方法との2通りを試します。

## Apache Tika Parsersを導入する
Mavenから導入します。
検証には`1.21`を使いました。

- [Maven Repository: org\.apache\.tika » tika\-parsers](https://mvnrepository.com/artifact/org.apache.tika/tika-parsers)

## `xml`系データのみをパースする方法
`OOXMLParser`を用いてパースすることでできます。
パースした結果は`ContentHandler`（今回は`BodyContentHandler`）に格納されます。
`BodyContentHandler`からの情報取得は`toString`以外の方法を見つけられませんでした。

サンプルコードは以下の通りです。
`src/main/resources`に入っているxml系ファイルをパースします。
注意点として、xml系でない（=`OOXMLParser`がパースできない）ファイルを入れると落ちます。

実行結果はTikaにファイル種別を検出してもらって自動でパースする方法と同じなので割愛します。

```kotlin
import org.apache.tika.parser.ParseContext
import org.apache.tika.parser.microsoft.ooxml.OOXMLParser
import org.apache.tika.sax.BodyContentHandler
import org.apache.tika.metadata.Metadata as TikaMetadata
import java.io.File
import java.io.FileInputStream

fun main() {
    val parser = OOXMLParser() // XML系データ用のパーサー
    val metaData = TikaMetadata()
    val context = ParseContext()
    val handler = BodyContentHandler() // パース結果が格納されるハンドラ

    File(System.getProperty("user.dir") + "/src/main/resources").listFiles().forEach {
        parser.parse(FileInputStream(it), handler, metaData, context)
        println(handler.toString())
    }
}
```

## Tikaにファイル種別を検出してもらって自動でパースする方法
先ほどのサンプルコードからパーサーを`AutoDetectParser`に差し替えることで実現できます。

```kotlin
import org.apache.tika.parser.AutoDetectParser
import org.apache.tika.parser.ParseContext
import org.apache.tika.sax.BodyContentHandler
import org.apache.tika.metadata.Metadata as TikaMetadata
import java.io.File
import java.io.FileInputStream

fun main() {
    val parser = AutoDetectParser() // ファイル種別まで自動検出するパーサー
    val metaData = TikaMetadata()
    val context = ParseContext()
    val handler = BodyContentHandler() // パース結果が格納されるハンドラ

    File(System.getProperty("user.dir") + "/src/main/resources").listFiles().forEach {
        println("-------------")
        println(it.name)

        parser.parse(FileInputStream(it), handler, metaData, context)
        println(handler.toString())
        println("-------------")
    }
}
```

### 実行結果
スプレッドシートで落とせるzip以外のファイル形式を全て回してみました。

```
-------------
xls_for_test.xlsx
シート1
	hoge	ふが	ピヨ
	fizz	バッズ	fizzBuzz



-------------
-------------
xls_for_test - シート1.csv
シート1
	hoge	ふが	ピヨ
	fizz	バッズ	fizzBuzz


hoge,ふが,ピヨ
fizz,バッズ,fizzBuzz

-------------
-------------
xls_for_test.ods
シート1
	hoge	ふが	ピヨ
	fizz	バッズ	fizzBuzz


hoge,ふが,ピヨ
fizz,バッズ,fizzBuzz
	hoge
	ふが
	ピヨ
	
	fizz
	バッズ
	fizzBuzz
	
	
	

???
Page 
??? (???)
00/00/0000, 00:00:00
Page  / 

-------------
-------------
xls_for_test - シート1.pdf
シート1
	hoge	ふが	ピヨ
	fizz	バッズ	fizzBuzz


hoge,ふが,ピヨ
fizz,バッズ,fizzBuzz
	hoge
	ふが
	ピヨ
	
	fizz
	バッズ
	fizzBuzz
	
	
	

???
Page 
??? (???)
00/00/0000, 00:00:00
Page  / 

hoge ふが ピヨ

fizz バッズ fizzBuzz



-------------
-------------
xls_for_test - シート1.tsv
シート1
	hoge	ふが	ピヨ
	fizz	バッズ	fizzBuzz


hoge,ふが,ピヨ
fizz,バッズ,fizzBuzz
	hoge
	ふが
	ピヨ
	
	fizz
	バッズ
	fizzBuzz
	
	
	

???
Page 
??? (???)
00/00/0000, 00:00:00
Page  / 

hoge ふが ピヨ

fizz バッズ fizzBuzz


hoge	ふが	ピヨ
fizz	バッズ	fizzBuzz

-------------
```

# 感想
とりあえずxlsxに限らず様々な形式のファイルがパースできてよかったです。
ただ、パースした結果の出力は`BodyContentHandler`の形式だと辛いので、より目的に合致するものが無いか探していきます。