文章目录
一、运行环境
- windows10
- IDEA 2022
- JDK 8
- Maven 3.8.6
- Apache POI 5
- fastjson2
二、需求描述
定制小程序开发写一个功能,任意json生成excel,定制小程序开发每个数组都单独生成一个sheet。
三、实现思路
参考资料:
定制小程序开发主要实现思路: 使用支持Java对象与、定制小程序开发字符串互相转换的fastjson,以及支持Java将JSON转化Excel的库 apache-poi
Excel定制小程序开发表格关键结构:
- Workbook 工作台,定制小程序开发相当于一个 excel文件
- sheet,一个excel定制小程序开发文件中的表格页面,定制小程序开发可能有多个
- row,所在sheet中的行
- cel,所在sheet定制小程序开发中所在行的列
- value,定制小程序开发所在单元格的值
- cel,所在sheet定制小程序开发中所在行的列
- row,所在sheet中的行
JSON转换的几种情形与实现思路:
情形一:普通的单层结构,多个JSON对象
{ "班级A" : [{ "文章":"课文1", "作者":"李白" }, { "文章":"课文2", "作者":"小李" }, { "文章":"课文2", "作者": "小明" }]}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
导出结果:
当我们使用遍历JSONObject时,每次读取到的都是单个{ } 所包含的对象,比如:
{ "文章":"课文1", "作者":"李白"}
- 1
- 2
- 3
- 4
这种情况下,我们在Excel的Sheet中的行是确定的,比如这里就是第二行(第一行是列名),行根据遍历的顺序确定,而列则是不确定的,在这里有 “文章”,“作者” 这两个列,但是一开始这两个列是不存在的。这里则确定文章在第一列,作者按第二列(默认升序排序)。
当遍历下一个对象时,我们可能遇到旧的列,也可能遇到新的列,比如:
{ "文章":"课文2", "作者":"李白", "出版日期": "2022年7月6日"}
- 1
- 2
- 3
- 4
- 5
这时,我们需要知道"文章" 和 “作者” 在第几列,同时也要知道 “出版日期” 应该在第几列,否则就不能确定唯一的单元格,将 JSON的value存储进去。
这里可以使用 Map<String, Integer> map
来记录列名以及下标。
在遍历对象时,key是列名,value则是单元格该填的值,如果 map.get(key) 的结果是空的,说明该列不存在,则需要创建,如果存在,那么可以创建单元格的对象,将值填入即可。
情形二:嵌套结构,JSON数组的嵌套
{ "班级A":[ { "学号":"A01", "语文":[ { "文章":"课文1", "作者":"李白" }, { "文章":"课文2", "作者":"小李" }, { "文章":"课文2", "作者": "小明" } ], "数学":"130" }, { "学号":"A02", "语文":"130", "数学":"135" } ],}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
实现效果:
这里相比之前的情况复杂了一些,主要的就是需要再次创建一个新的 sheet,这意味着需要使用递归完成创建,于是我们可以将之前那种情形的代码实现封装成一个方法,比如createSubSheet(),在遍历JSON对象时,如果value值是一个JSONAarray,那么就再次调用createSubSheet()这个方法,只要使用同一个Workbook对象,表示同一个excel文件,就能满足这个需求了。
四、实现代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>groupId</groupId> <artifactId>poi_demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.7</version> </dependency> </dependencies></project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
JSONToExcelUtil.java
package cn.uni;import com.alibaba.fastjson2.JSON;import com.alibaba.fastjson2.JSONArray;import com.alibaba.fastjson2.JSONObject;import com.alibaba.fastjson2.JSONWriter;import com.alibaba.fastjson2.schema.JSONSchema;import org.apache.commons.io.FileUtils;import org.apache.commons.lang3.StringUtils;import org.apache.poi.ss.usermodel.Workbook;import org.apache.poi.xssf.usermodel.XSSFCell;import org.apache.poi.xssf.usermodel.XSSFRow;import org.apache.poi.xssf.usermodel.XSSFSheet;import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.*;import java.util.HashMap;import java.util.Map;/** * uni * 2022/07/05~2022/07/06 * 将 JSON 转化为 Excel的工具类 */public class JSONToExcelUtil { /** * 读取绝对路径下的json文件 * @param resourcePath json文件的绝对路径 * @return json文件格式化后的字符串 */ public static String readJSONFile(String resourcePath) { try{ // 1. 创建文件流 File file = new File(resourcePath); // 2. 使用 common-lang3工具包, 以 UTF-8 格式读取文件, 转为字符串 String str = FileUtils.readFileToString(file, "UTF-8"); JSONObject jsonObject = JSONObject.parseObject(str); // 3. 将字符串转为标准的JSON格式的字符串 return JSONObject.toJSONString(jsonObject, JSONWriter.Feature.WriteMapNullValue); } catch (IOException e) { throw new RuntimeException(e); } } /** * 创建 Sheet * @param layer 当前Sheet所在JSON中的层级 * @param workbook 工作台 ( excel表格的主体 ) * @param sheetName 当前页的名称 * @param jsonArray JSON数组 */ public static void createSubSheet(int layer, XSSFWorkbook workbook, String sheetName, JSONArray jsonArray){ // 创建新的 sheet XSSFSheet sheet = workbook.createSheet(sheetName); // 存储每个字段 Map<String, Integer> map = new HashMap<>(); // 统计当前的列 int cellCount = 0; // 创建第一行 XSSFRow firstRow = sheet.createRow(0); // 获取每一项 for (int row = 1; row <= jsonArray.size(); row++) { JSONObject jsonObject = jsonArray.getJSONObject(row - 1); // 创建行 XSSFRow currentRow = sheet.createRow(row); if(jsonObject != null){ // 遍历每个KV for (String cellName : jsonObject.keySet()) { // 列不存在时, 则创建列 if (!map.containsKey(cellName)) { // 第一行创建列 XSSFCell firstRowCell = firstRow.createCell(cellCount); firstRowCell.setCellValue(cellName); map.put(cellName, cellCount++); } // 设置单元格 XSSFCell cell = currentRow.createCell(map.get(cellName)); // 获取 Value String cellValue = JSON.toJSONString(jsonObject.get(cellName)); // 如果V为数组则递归创建sheet if(JSON.isValidArray(cellValue)){ String subCellName = sheetName + "-" + cellName; cell.setCellValue(subCellName); createSubSheet(layer + 1, workbook,subCellName, jsonObject.getJSONArray(cellName)); } else{ cell.setCellValue(jsonObject.getString(cellName)); } } } else{ // Value为一个数组 JSONArray array = jsonArray.getJSONArray(row - 1); // 遍历数组 if(array != null && array.size() > 0){ for (int i = 1; i <= array.size(); i++) { JSONObject obj = array.getJSONObject(i - 1); // 遍历 obj for (String cellName : obj.keySet()) { // 若列不存在则添加 if(!map.containsKey(cellName)){ XSSFCell cell = firstRow.createCell(cellCount); map.put(cellName, cellCount++); cell.setCellValue(cellName); } // 分情况讨论 String cellValue = obj.getString(cellName); XSSFCell cell = currentRow.createCell(map.get(cellName)); // 如果值是JSON对象, 则递归创建 if(JSON.isValidObject(cellValue)){ String subSheetName = sheetName + "-" + cellName; cell.setCellValue(subSheetName); createSubSheet(layer+1, workbook, subSheetName , JSONObject.parseObject(cellValue)); } else if(JSON.isValidArray(cellValue)){ String subSheetName = sheetName + "-" + cellName; cell.setCellValue(subSheetName); createSubSheet(layer+1, workbook, subSheetName , JSONArray.parseArray(cellValue)); } else { cell.setCellValue(cellValue); } } } } else { firstRow.createCell(0).setCellValue(sheetName); XSSFCell cell = currentRow.createCell(cellCount); cell.setCellValue(jsonArray.getString(row-1)); } } } } /** * 创建 Sheet * @param layer 当前Sheet所在JSON中的层级 * @param workbook 工作台 ( excel表格的主体 ) * @param sheetName 当前页的名称 * @param jsonObject JSON对象 */ public static void createSubSheet(int layer, XSSFWorkbook workbook, String sheetName, JSONObject jsonObject){ // 创建新的 sheet XSSFSheet sheet = workbook.createSheet(sheetName); // 存储每个字段 Map<String, Integer> map = new HashMap<>(); // 统计当前的列 int cellCount = 0; // 创建第一行 XSSFRow fistRow = sheet.createRow(0); // 记录行数 int row = 1; // 获取每一项 // 创建行 XSSFRow currentRow = sheet.createRow(row); // 遍历每个KV for (String cellName : jsonObject.keySet()) { // 列不存在时, 则创建列 if (!map.containsKey(cellName)) { // 第一行创建列 XSSFCell firstRowCell = fistRow.createCell(cellCount); firstRowCell.setCellValue(cellName); map.put(cellName, cellCount++); } // 设置单元格 XSSFCell cell = currentRow.createCell(map.get(cellName)); // 获取 Value String cellValue = JSON.toJSONString(jsonObject.get(cellName)); // 如果V为对象则递归创建sheet if(JSON.isValidObject(cellValue)){ String subCellName = "Sheet" + layer + "-" + sheetName + "-" + cellName; cell.setCellValue(subCellName); createSubSheet(layer + 1, workbook,subCellName, JSON.parseObject(cellValue)); } else if(JSON.isValidArray(cellValue)){ String subCellName = "Sheet" + layer + "-" + sheetName + "-" + cellName; cell.setCellValue(subCellName); createSubSheet(layer + 1, workbook,subCellName, JSON.parseArray(cellValue)); } else{ cell.setCellValue(jsonObject.getString(cellName)); } } } /** * 将格式化的JSON字符串导出为Excel * @param jsonStr 格式化后的JSON字符串 * @param savePath Excel保存路径 * @param excelName Excel名称 */ public static void toExcelByString(String jsonStr, String savePath, String excelName){ assert JSON.isValid(jsonStr) : "字符串: " + jsonStr + " 不是标准的JSON字符串"; toExcelByJSONObject(JSONObject.parseObject(jsonStr),savePath, excelName); } /** * 将普通的Java对象导出为JSON文件 * @param obj Java对象 * @param savePath Excel保存路径 * @param excelName Excel名称 */ public static void toExcelByObject(Object obj, String savePath, String excelName){ String jsonStr = JSON.toJSONString(obj, JSONWriter.Feature.WriteMapNullValue); JSONObject jsonObject = JSONObject.parseObject(jsonStr); toExcelByJSONObject(jsonObject, savePath, excelName); } /** * 将本地的JSON文件导出为 Excel * @param resourcePath JSON文件的绝对路径 * @param savePath 保存的路径 * @param excelName 保存的Excel名称 */ public static void toExcelByLocalJSONFile(String resourcePath, String savePath, String excelName){ // 1. 获取标准的 JSON 字符串 String jsonStr = readJSONFile(resourcePath); // 验证字符串是否合法 assert JSON.isValid(jsonStr) : "路径:[" + resourcePath + "] 的json文件不符合标准的JSON格式"; toExcelByString(jsonStr, savePath, excelName); } /** * 将JSONObject转化导出到 Excel * 这里遵循递归导出,当遇到数组时会调用 createSheet创建新的页面。 * @param jsonObject JSON对象 * @param savePath Excel保存路径 * @param excelName Excel名称 */ public static void toExcelByJSONObject(JSONObject jsonObject, String savePath, String excelName){ try(XSSFWorkbook workbook = new XSSFWorkbook()){ // 获取当前的Sheet XSSFSheet sheet = workbook.createSheet("sheet"); // 获取第一行 XSSFRow firstRow = sheet.createRow(0); // 记录Key所在的列 Map<String, Integer> map = new HashMap<>(); // 记录列数 int cellCount = 0; // 遍历 JSON的key XSSFRow currentRow = sheet.createRow(1); for (String key : jsonObject.keySet()) { // 先处理列 if(!map.containsKey(key)){ // 当列不存在则添加 map.put(key, cellCount); XSSFCell cell = firstRow.createCell(cellCount++); cell.setCellValue(key); } XSSFCell currentCell = currentRow.createCell(map.get(key)); String jsonStr = jsonObject.getString(key); // 如果 Value为数组 则创建新的 Sheet if(JSON.isValidArray(jsonStr)){ String subSheetName = "Sheet-" + key; createSubSheet(1, workbook, subSheetName, jsonObject.getJSONArray(key)); currentCell.setCellValue(subSheetName); } else if(JSON.isValidObject(jsonStr)){ // 如果当前 value 仍然是一个JSON对象 String subSheetName = "Sheet-" + key; createSubSheet(1, workbook, subSheetName, jsonObject.getJSONObject(key)); currentCell.setCellValue(subSheetName); } else { // 特殊处理空值 if(StringUtils.isEmpty(jsonStr)) currentCell.setCellValue("null"); else currentCell.setCellValue(jsonStr); } } save(workbook, savePath, excelName); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * 将 Excel对象保存到本地 * @param workbook Excel对象 * @param path Excel文件路径 * @param excelName excel名称 */ public static void save(Workbook workbook, String path, String excelName){ try { FileOutputStream fileOutputStream = new FileOutputStream(path +"/" + excelName +".xlsx"); workbook.write(fileOutputStream); fileOutputStream.close(); System.out.println("保存完毕. 保存位置为[ " + path + "/" + excelName + " ]"); } catch (IOException e) { throw new RuntimeException(e); } } public static void main(String[] args) { String jsonPath = "C:\\Users\\unirithe\\IdeaProjects\\poi_demo\\src\\main\\resources\\data.json"; String savePath = "C:\\Users\\unirithe\\Desktop"; String excelName = "demo"; // 测试1 toExcelByLocalJSONFile(jsonPath, savePath, excelName + "1"); String jsonStr = readJSONFile(jsonPath); JSONObject jsonObject = JSONObject.parseObject(jsonStr); Object object = JSON.parse(jsonStr); // 测试2 toExcelByString(jsonStr, savePath, excelName + "2"); // 测试3 toExcelByObject(object, savePath, excelName + "3"); // 测试4 toExcelByJSONObject(jsonObject, savePath, excelName + "4"); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
测试的JSON数据:
{ "班级A":[ { "学号":"A01", "语文":[ { "文章":"课文1", "作者":"李白" }, { "文章":"课文2", "作者":"小李" }, { "文章":"课文2", "作者": "小明" } ], "数学":"130" }, { "学号":"A02", "语文":"130", "数学":"135" } ], "班级B":[ { "学号":"B01", "语文":"128", "数学":"135" }, { "学号":"B02", "语文":"133", "数学":"140" } ]}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
测试结果如下,这里保存的demo1、demo2、demo3和demo4结果是一致的,主要是为了测试不同方法的正确性。