写在前面: 博主是一名大数据行业的追梦人,昵称来源于《爱丽丝梦游仙境》中的Alice和自己的昵称。作为一名互联网小白,
写博客一方面是为了记录自己的学习历程,一方面是希望能够帮助到很多和自己一样处于起步阶段的萌新
。由于水平有限,博客中难免会有一些错误,有纰漏之处恳请各位大佬不吝赐教!个人小站:http://alices.ibilibili.xyz/ , 博客主页:https://alice.blog.csdn.net/
尽管当前水平可能不及各位大佬,但我还是希望自己能够做得更好,因为一天的生活就是一生的缩影
。我希望在最美的年华,做最好的自己
!
最近公司有一个需求,需要解析Kylin上某个Cube的JSON格式的数据,并输出到Excel文件中。
我们先来看看这个Cube内部都有些什么?
这里我以其中一个JSON文件为例
是不是JSON内部的层级关系有点混乱,没关系,我们将里面的内容放到网页上去解析看看。
我们想要操作的是 key值为 indexes
下的数组,并对 key = layouts
下的 id
和col_order
集合 拿出来,并对col_order集合中的元素做一个过滤,只获取其中 < 100000的元素,并将其输出到 Excel 文件中。
现在似乎需求已经看懂了,那我们就开始上手代码吧。
首先我们先创建一个 Maven 项目,因为涉及到JSON的解析,我们先在Pom中导入相关坐标:
<dependencies>
<dependency>
<groupId>org.jsongroupId>
<artifactId>jsonartifactId>
<version>20090211version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.47version>
dependency>
dependencies>
然后我们创建一个ParseJson类,在main方法中,我们先定义好动态参数。
// 输入路径
String fileIntputPath = "G:\\idea arc\\ParseJson\\src\\main\\resources\\test.json";
// 输出路径
String fileOutputPath = "G:\\idea arc\\ParseJson\\src\\main\\resources\\writeTest2.xlsx";
// 限制大小
int limitNumber = 100000;
因为我们需要根据 指定的输入路径 去本地读取 JSON数据,所以我们还需要写一个方法。
/**
* 读取指定文件路径的内容
* @param filePath 文件路径
* @return 文件内容
*/
private static String readJsonFile(String filePath) {
String jsonStr = "";
try {
File jsonFile = new File(filePath);
FileReader fileReader = new FileReader(jsonFile);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile),"utf-8");
int ch = 0;
StringBuilder sb = new StringBuilder();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
有了读取文件内容的方法,我们就可以在接下来自己书写一个解析 JSON 文件的方法了。根据刚在网页上展示的层级关系图,我们不难得出写出以下代码:
// 调用方法,获取到指定路径的文件内容
String str = readJsonFile(filePath);
// 调用 JSON 库的内容,获取到解析的对象
JSONObject jsonObject = JSON.parseObject(str);
// 获取到 indexes 数组
JSONArray jsonArray = jsonObject.getJSONArray("indexes");
for (Object o : jsonArray) {
JSONArray layouts = ((JSONObject) o).getJSONArray("layouts");
for (Object layout : layouts) {
int id = ((JSONObject) layout).getIntValue("id");
JSONArray colOrder = ((JSONObject) layout).getJSONArray("col_order");
// 定义一个 StringBuilder,用于保存每次累加的结果
StringBuilder stringBuilder = new StringBuilder();
// 定义一个字段 size 保存原来数组的长度
int size = colOrder.size();
// 调用自己写的静态方法,获取到满足需求的数组长度
int greaterThanlakh = getGreaterThan(colOrder, size,limitNumber);
// 定义一个字段保存每次循环的次数
int loopCount = 0;
for (Object o1 : colOrder) {
// 每循环一次,loopCount数值+1
loopCount ++;
// 将其转换成 int 类型的数字
int number = Integer.parseInt(o1.toString());
if(number < 100000){
if (loopCount==greaterThanlakh){
stringBuilder.append(number);
}else{
stringBuilder.append(number).append(",");
}
}
}
}
}
}
在这个过程中,因为涉及到判断一个数组中,元素没有被过滤的个数,所以又自己写的一个功能方法。
/**
* 计算出元素中小于100000的元素个数
* @param jsonArray JSON数组
* @param size JSON数组的容量大小
* @param limitNumber 过滤条件
* @return 小于100000的元素个数
*/
private static int getGreaterThan (JSONArray jsonArray,int size,int limitNumber){
// 定义一个变量保存数组中 > 100000 的元素个数
int numberCount = 0;
for (Object o : jsonArray) {
int number = Integer.parseInt(o.toString());
if (number >= limitNumber){
numberCount ++;
}
}
return size - numberCount;
}
现在我们已经获取到了每一个id ,以及它所对应小于 100000 的 col_order数组中的元素。那么我们就应该开始考虑一下,如何将这些值输出到Excel文件中。
poi 组件是由Apache提供的组件包,主要职责是为我们的Java程序提供对于office文档的相关操作。
但是像菌这样的小白,一看到这些常用的类,还不吓得原地昏厥。
所以说,这辈子都不可能用的。但是需求还没完全实现,我们该怎么办呢?
正当本菌一筹莫展之际,突然在经友人的提醒下,想起了在GitHub上一个神奇的仓库。
https://github.com/looly/hutool
可以看到,目前该开源项目,已经斩获了 15.3k Star。
根据作者介绍,Hutool 的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug。
关于 Hutool 在 maven 项目中的使用也非常简单,我们只需要在项目的pom.xml的dependencies中加入以下内容:
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.4.1version>
dependency>
关于更多 Hutool 的具体使用,我们可以去参考 中文手册
因为我们需要参考如何生成Excel,我们可以定位到这个位置
我们先定义一个嵌套的List,List的元素也是一个List,内层的一个List代表一行数据,每行都有4个单元格,最终list
对象代表多行数据。
List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");
List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
然后我们创建ExcelWriter
对象后写出数据:
//通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter("d:/writeTest.xlsx");
//通过构造方法创建writer
//ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");
//跳过当前行,既第一行,非必须,在此演示用
writer.passCurrentRow();
//合并单元格后的标题行,使用默认标题样式
writer.merge(row1.size() - 1, "测试标题");
//一次性写出内容,强制输出标题
writer.write(rows, true);
//关闭writer,释放内存
writer.close();
运行一下程序,我们观察案例代码实现的效果,打开 writeTest.xlsx
可谓是非常的 nice,我们只需要根据案例代码所给的提示把我们之前的代码 "完善"一下就好了。
但是还需要注意一点的就是,
Hutool-poi是针对Apache POI的封装,因此需要用户自行引入POI库,Hutool默认不引入。到目前为止,Hutool-poi支持:
- Excel文件(xls, xlsx)的读取(ExcelReader)
- Excel文件(xls,xlsx)的写出(ExcelWriter)
如果我们想要输出Excel,推荐引入poi-ooxml,这个包会自动关联引入poi包,且可以很好的支持Office2007+的文档格式
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>${poi.version}version>
dependency>
如果需要使用Sax方式读取Excel,需要引入以下依赖:
<dependency>
<groupId>xercesgroupId>
<artifactId>xercesImplartifactId>
<version>${xerces.version}version>
dependency>
说明 hutool-4.x的poi-ooxml 版本需高于
3.17
(别问我3.8版本为啥不行,因为3.17 > 3.8 ) hutool-5.x的poi-ooxml 版本需高于4.1.2
xercesImpl版本高于2.12.0
引入后即可使用Hutool的方法操作Office文件了,下面贴出正式的代码:
package com.czxy;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.io.*;
import java.util.List;
/**
* @Author: Alice菌
* @Date: 2020/9/7 18:27
* @Description:
*/
public class ParseJson {
public static void main(String[] args) throws Exception {
// 输入路径
//String fileIntputPath = "G:\\idea arc\\ParseJson\\src\\main\\resources\\test.json";
String fileIntputPath = args[0];
// 输出路径
//String fileOutputPath = "G:\\idea arc\\ParseJson\\src\\main\\resources\\writeTest2.xlsx";
String fileOutputPath = args[1];
// 限制大小
//int limitNumber = 100000;
int limitNumber = Integer.parseInt(args[2]);
strWriteToJSONObject(fileIntputPath,fileOutputPath,limitNumber);
}
/**
* 读取指定文件路径的内容
* @param filePath 文件路径
* @return 文件内容
*/
private static String readJsonFile(String filePath) {
String jsonStr = "";
try {
File jsonFile = new File(filePath);
FileReader fileReader = new FileReader(jsonFile);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile),"utf-8");
int ch = 0;
StringBuilder sb = new StringBuilder();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static void strWriteToJSONObject(String filePath,String fileOutPut,int limitNumber) throws Exception {
// 调用方法,获取到指定路径的文件内容
String str = readJsonFile(filePath);
// 调用 JSON 库的内容,获取到解析的对象
JSONObject jsonObject = JSON.parseObject(str);
// 获取到 indexes 数组
JSONArray jsonArray = jsonObject.getJSONArray("indexes");
//如果需要输出到 txt 文本中,则使用下面这种方式
//FileWriter fw = new FileWriter(fileOutPut, true);
//BufferedWriter bw = new BufferedWriter(fw);
//初始化一个集合,用于存储所有需要输出到Excel的列
List<List<String>> rows = CollUtil.newArrayList();
for (Object o : jsonArray) {
JSONArray layouts = ((JSONObject) o).getJSONArray("layouts");
for (Object layout : layouts) {
int id = ((JSONObject) layout).getIntValue("id");
JSONArray colOrder = ((JSONObject) layout).getJSONArray("col_order");
// 定义一个 StringBuilder,用于保存每次累加的结果
StringBuilder stringBuilder = new StringBuilder();
// 定义一个字段 size 保存原来数组的长度
int size = colOrder.size();
// 调用自己写的静态方法,获取到满足需求的数组长度
int greaterThanlakh = getGreaterThan(colOrder, size,limitNumber);
// 定义一个字段保存每次循环的次数
int loopCount = 0;
for (Object o1 : colOrder) {
// 每循环一次,loopCount数值+1
loopCount ++;
// 将其转换成 int 类型的数字
int number = Integer.parseInt(o1.toString());
if(number < 100000){
if (loopCount==greaterThanlakh){
stringBuilder.append(number);
}else{
stringBuilder.append(number).append(",");
}
}
}
List<String> row = CollUtil.newArrayList(id+"",stringBuilder.toString());
rows.add(row);
}
}
OutToExcel(rows,fileOutPut);
}
/**
* 计算出元素中小于100000的元素个数
* @param jsonArray JSON数组
* @param size JSON数组的容量大小
* @param limitNumber 过滤条件
* @return 小于100000的元素个数
*/
private static int getGreaterThan (JSONArray jsonArray,int size,int limitNumber){
// 定义一个变量保存数组中 > 100000 的元素个数
int numberCount = 0;
for (Object o : jsonArray) {
int number = Integer.parseInt(o.toString());
if (number >= limitNumber){
numberCount ++;
}
}
return size - numberCount;
}
public static void OutToExcel(List<List<String>> rows,String fileOutPut){
for (List<String> row : rows) {
System.out.println(row);
}
System.out.println("fileOutPath:"+fileOutPut);
//通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(fileOutPut);
//通过构造方法创建writer
//ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");
//跳过当前行,既第一行,非必须,在此演示用
//writer.passCurrentRow();
//合并单元格后的标题行,使用默认标题样式
//writer.merge(row1.size() - 1, "测试标题");
//一次性写出内容,强制输出标题
writer.write(rows, true);
//关闭writer,释放内存
writer.close();
}
}
细心的朋友们可能已经发现,博主已经将 main 方法中的变量替换成了参数,主要的目的就是可以将代码打包到Linux上运行,就像这样。
这里我们打开 outToExcel.xlsx 文件,看下效果。
本篇博客,博主主要为大家介绍了如何通过Json去解析Cube中的数据,并将需要的数据输出到Excel当中。菌着重为大家安利了一款非常实用的工具库——hutool,希望大家都能在不断探索的过程中,发现一些新鲜好玩的东西。
如果以上过程中出现了任何的纰漏错误,烦请大佬们指正
受益的朋友或对大数据技术感兴趣的伙伴记得点赞关注支持一波
希望我们都能在学习的道路上越走越远