【Kotlin】【Windows】JavaFX + Apache POI + GsonでExcelからJsonファイルを作る2 (1の修正)
はじめに
前回で一応Excelからデータを取得してJsonファイルに書き出す、ということができるようになりました。
その時以下の内容で出力するようにしていました。
{ data1:{ "element1": "element1" ,"element2": "element2" } , data2{ "element1": "element1" ,"element2": "element2" } ~省略~
しかし、Androidのアプリで同じくGsonを使ってこのJsonを読み込もうとしたときに、以下のような配列として出力されている方が良い、ということがわかってきました。
{ data:[ { "element1": "element1" ,"element2": "element2" } , { "element1": "element1" ,"element2": "element2" } ~省略~ ] }
ということで今回は、Excelから読み込んだデータを配列として出力する方法についてです。
データをセットするクラスを使用する
配列を作るには、専用のクラスを作成してその配列をJsonに変換 → 出力 という方法が考えられます。
ToiletInfo.kt
class ToiletInfo { var toiletName = "" get set var district = "" get set var municipality = "" get set var address = "" get set var latitude: Double = 0.0 get set var longitude: Double = 0.0 get set var availableTime = "" get set var hasMultiPurposeToilet: Boolean = false get set }
このクラスを使って、Excelから読み込んだデータをセットした配列を作成します。
SpreadsheetAccesser.kt
class SpreadsheetAccesser { lateinit var ToiletInfoList: ArrayList< ToiletInfo > get ~省略~ fun loadFile(targetFilePath: String, targetSheetName: String){ val fileStream = FileInputStream(targetFilePath) val currentWorkbook = WorkbookFactory.create(fileStream) if(currentWorkbook == null){ return } val targetSheet: Sheet? = currentWorkbook.getSheet(targetSheetName) if(targetSheet == null){ return } val rowCount = targetSheet.physicalNumberOfRows - 1 if(rowCount < 0){ return } // 最初の行から列数を取得する. if(targetSheet.getRow(0).physicalNumberOfCells >= 9) { ToiletInfoList = ArrayList() // 最初の行は項目名なのでスキップ. for (i in 1..rowCount) { val toiletInfo = ToiletInfo() // 1. toiletName, 2. district, 3. municipality, 4. address, // 5. latitude, 6. longitude, 7.availableTime, 8.hasMultiPurposeToilet. toiletInfo.toiletName = targetSheet.getRow(i).getCell(1).stringCellValue toiletInfo.district = targetSheet.getRow(i).getCell(2).stringCellValue toiletInfo.municipality = targetSheet.getRow(i).getCell(3).stringCellValue toiletInfo.address = targetSheet.getRow(i).getCell(4).stringCellValue toiletInfo.latitude = targetSheet.getRow(i).getCell(5).numericCellValue toiletInfo.longitude = targetSheet.getRow(i).getCell(6).numericCellValue var availableTime: String? = targetSheet.getRow(i).getCell(7)?.stringCellValue availableTime = availableTime?: "" toiletInfo.availableTime = availableTime toiletInfo.hasMultiPurposeToilet = targetSheet.getRow(i).getCell(8).booleanCellValue ToiletInfoList.add(toiletInfo) } } currentWorkbook.close() fileStream.close() } }
あとはJsonに変換し、出力します。
JsonFileCreater.kt
class JsonFileCreater { fun createFile(toiletInfoList: ArrayList< ToiletInfo >, fileTitle: String){ val stringWriter = StringWriter() val jsonWriter = JsonWriter(BufferedWriter(stringWriter)) // 出力したJsonファイルで適切にインデントが入る...はずだが今回は効いていないようです. jsonWriter.setIndent(" ") jsonWriter.beginObject() val gson = Gson() // 「jsonWriter.name("").value("")」のvalueにはArrayListを入れられないため、Jsonに変換した上でJsonデータとしてセット. jsonWriter.name("toiletInfo").jsonValue(gson.toJson(toiletInfoList)) jsonWriter.endObject() jsonWriter.close() val createdJson = String(stringWriter.buffer) try{ val splittedTitles = fileTitle.split('.') if(splittedTitles.size <= 0){ return } val fileWriter = FileWriter(splittedTitles[0] + ".json") fileWriter.write(createdJson) fileWriter.close() // TODO: 出力が終わったことを知らせる. }catch(e: IOException){ // TODO: 適切なエラー処理. } } }
出力した結果は以下のようになります。
{ "toiletInfo": [ { "toiletName": "友ヶ島野奈浦公衆トイレ", "district": "和歌山県", "municipality": "和歌山市", "address": "和歌山市加太笘ヶ沖島2673-3", "latitude": 34.282891, "longitude": 135.008677, "availableTime": "終日", "hasMultiPurposeToilet": true }, { "toiletName": "友ヶ島南垂水公衆トイレ", "district": "和歌山県", "municipality": "和歌山市", "address": "和歌山市加太苫ケ沖島2673-1", "latitude": 34.28255, "longitude": 135.013597, "availableTime": "終日", "hasMultiPurposeToilet": false }, ~省略~
HashMapを使う(失敗)
上記の方法を取ると確かにやりたいことの実現はできたのですが、読み込むセルの列数やデータ型などが固定されてしまうため少々不便でもあります。
それを解決する方法を調べていたのですが、HashMap使えるんじゃね?と思い至り、試してみることにしました。
SpreadsheetAccesser.kt
import javafx.collections.FXCollections import javafx.collections.ObservableList import org.apache.poi.ss.usermodel.Cell import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.WorkbookFactory import java.io.FileInputStream import java.util.* class SpreadsheetAccesser { lateinit var LoadedSheetItemList: ArrayList < ArrayList < HashMap < String, Any > > > get ~省略~ fun loadFile(targetFilePath: String, targetSheetName: String){ val fileStream = FileInputStream(targetFilePath) val currentWorkbook = WorkbookFactory.create(fileStream) if(currentWorkbook == null){ return } // シート名から対象のシートを取得する. val targetSheet: Sheet? = currentWorkbook.getSheet(targetSheetName) if(targetSheet == null){ return } // セルに何らかの値が含まれる行数の取得. val rowCount = targetSheet.physicalNumberOfRows - 1 if(rowCount < 0){ return } // 最初の行から列数を取得する. val columnCount = targetSheet.getRow(0).physicalNumberOfCells - 1 // 最初の行をタイトル行としてArrayListを作成する. val ColumnTitleList = ArrayList < String > () for(cell in targetSheet.getRow(0)){ ColumnTitleList.add(getCellValue(cell).toString()) } // 実際の値が入ったセルの値をセットするArrayListの生成. LoadedSheetItemList = ArrayList < ArrayList < HashMap < String, Any > > > () // タイトル行から取得した列数 ✕ (セルに値が含まれる行数 - 1)の値をセットする. for(i in 1..rowCount){ val loadedRowItemList = ArrayList < HashMap < String, Any > > () for(t in 1..columnCount){ loadedRowItemList.add(hashMapOf < String, Any > (ColumnTitleList[t] to getCellValue(targetSheet.getRow(i).getCell(t)))) } LoadedSheetItemList.add(loadedRowItemList) } currentWorkbook.close() fileStream.close() } fun getCellValue(targetCell: Cell?): Any{ var result = "" if(targetCell == null){ return result } // SpreadsheetにおけるCellの型に合わせて値を返す. when(targetCell.cellType){ Cell.CELL_TYPE_BOOLEAN -> return targetCell.booleanCellValue Cell.CELL_TYPE_NUMERIC -> return targetCell.numericCellValue Cell.CELL_TYPE_STRING -> return targetCell.stringCellValue Cell.CELL_TYPE_FORMULA -> return targetCell.cellFormula } return result } }
JsonFileCreater.kt
import com.google.gson.Gson import com.google.gson.stream.JsonWriter import java.io.BufferedWriter import java.io.FileWriter import java.io.IOException import java.io.StringWriter import java.util.* class JsonFileCreater { fun createFile(toiletInfoList: ArrayList < ArrayList < HashMap < String, Any > > >, fileTitle: String){ val stringWriter = StringWriter() val jsonWriter = JsonWriter(BufferedWriter(stringWriter)) jsonWriter.setIndent(" ") jsonWriter.beginObject() val gson = Gson() jsonWriter.name("toiletInfo").jsonValue(gson.toJson(toiletInfoList)) jsonWriter.endObject() jsonWriter.close() val createdJson = String(stringWriter.buffer) try{ val splittedTitles = fileTitle.split('.') if(splittedTitles.size <= 0){ return } val fileWriter = FileWriter(splittedTitles[0] + ".json") fileWriter.write(createdJson) fileWriter.close() }catch(e: IOException){ } } }
で、これを実行するとどうなるかというと、
{ "toiletInfo": [ { "toiletName": "友ヶ島野奈浦公衆トイレ" }, { "district": "和歌山県" }, { "municipality": "和歌山市" }, { "address": "和歌山市加太笘ヶ沖島2673-3" }, { "latitude": 34.282891 }, { "longitude": 135.008677 }, { "availableTime": "終日" }, { "hasMultiPurposeToilet": true }, { "toiletName": "友ヶ島南垂水公衆トイレ" }, { "district": "和歌山県" }, { "municipality": "和歌山市" }, { "address": "和歌山市加太苫ケ沖島2673-1" }, { "latitude": 34.28255 }, { "longitude": 135.013597 }, { "availableTime": "終日" }, { "hasMultiPurposeToilet": false }, ~省略~
う~ん、なんか違う(´;ω;`)
何かしら解決方法はありそうですが、今回調べただけではよくわかりませんでしたorz
これについては、今後解決方法が見つかったらこのブログで報告したいと思います。