【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
これについては、今後解決方法が見つかったらこのブログで報告したいと思います。