手把手教你JSON解析完Cube数据,如何输出到Excel

写在前面: 博主是一名大数据行业的追梦人,昵称来源于《爱丽丝梦游仙境》中的Alice和自己的昵称。作为一名互联网小白,写博客一方面是为了记录自己的学习历程,一方面是希望能够帮助到很多和自己一样处于起步阶段的萌新。由于水平有限,博客中难免会有一些错误,有纰漏之处恳请各位大佬不吝赐教!个人小站:http://alices.ibilibili.xyz/ , 博客主页:https://alice.blog.csdn.net/
尽管当前水平可能不及各位大佬,但我还是希望自己能够做得更好,因为一天的生活就是一生的缩影。我希望在最美的年华,做最好的自己

        最近公司有一个需求,需要解析Kylin上某个Cube的JSON格式的数据,并输出到Excel文件中。

        我们先来看看这个Cube内部都有些什么?

        这里我以其中一个JSON文件为例

在这里插入图片描述
        是不是JSON内部的层级关系有点混乱,没关系,我们将里面的内容放到网页上去解析看看。

在这里插入图片描述
        我们想要操作的是 key值为 indexes下的数组,并对 key = layouts 下的 idcol_order集合 拿出来,并对col_order集合中的元素做一个过滤,只获取其中 < 100000的元素,并将其输出到 Excel 文件中。

在这里插入图片描述
        现在似乎需求已经看懂了,那我们就开始上手代码吧。
        
在这里插入图片描述
        首先我们先创建一个 Maven 项目,因为涉及到JSON的解析,我们先在Pom中导入相关坐标:

<dependencies>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20090211</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </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文件中。

在这里插入图片描述
        可能熟悉Java的朋友能马上想起来 POI

        poi 组件是由Apache提供的组件包,主要职责是为我们的Java程序提供对于office文档的相关操作

        但是像菌这样的小白,一看到这些常用的类,还不吓得原地昏厥。
在这里插入图片描述
        所以说,这辈子都不可能用的。但是需求还没完全实现,我们该怎么办呢?

        正当本菌一筹莫展之际,突然在经友人的提醒下,想起了在GitHub上一个神奇的仓库。

        https://github.com/looly/hutool

        可以看到,目前该开源项目,已经斩获了 15.3k Star。

在这里插入图片描述
        该仓库中包含了对大部分常用功能的代码封装。
        
在这里插入图片描述

根据作者介绍,Hutool 的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug。

        关于 Hutool 在 maven 项目中的使用也非常简单,我们只需要在项目的pom.xml的dependencies中加入以下内容:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.1</version>
</dependency>

        关于更多 Hutool 的具体使用,我们可以去参考 中文手册

        因为我们需要参考如何生成Excel,我们可以定位到这个位置

在这里插入图片描述
        这里我将它的使用例子贴出来:

使用例子

1、将行列对象写出到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.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>${poi.version}</version>
</dependency>

        如果需要使用Sax方式读取Excel,需要引入以下依赖:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <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,希望大家都能在不断探索的过程中,发现一些新鲜好玩的东西。

        如果以上过程中出现了任何的纰漏错误,烦请大佬们指正😅

        受益的朋友或对大数据技术感兴趣的伙伴记得点赞关注支持一波🙏

        希望我们都能在学习的道路上越走越远😉
在这里插入图片描述

Alice菌 CSDN认证博客专家 CSDN博客专家 大数据学者 追梦人
00后大数据学者,坚信技术成就自我。
个人原创公众号【猿人菌】,每日更新大数据干货,欢迎上车!
一天的生活就是一生的缩影。在最美的年华,做最好的自己!
【课程内容】 所涉及的具体内容包括输入录入技巧、排序、筛选、函数公式、数据透视表、图表、汇总等功能应用,并结合大量的企业应用实例,以互动的方式将解决思路和操作过程逐一呈现。 【课程特点】 1、190节大容量课程:包含了Excel软件的大部分知识点; 2、创新的学模式:手把手您电子表格,一看就懂,一学就会; 3、美贴心的操作提示:让您的眼睛始终处于操作的焦点位置,不用再满屏找光标; 4、语言简洁精练:瞄准问题的核心所在,减少对思维的干扰,并节省您宝贵的时间; 5、视频短小精悍:即方便于您的学习和记忆,也方便日后对功能的检索; 【互动程的特点】 •互动性 -首创互动式学习,不同于以往的任何一种学习方式,不看视频不看书,轻松,高效,好玩 -手把手学,一步步引导操作,实时看到效果,就像自己在操作一样,信心满满 -即点即学,课程内可根据自己的需要,随时调整界面上方进度条,重复操作,加强记忆 -强大的学习跟踪系统,对整体学习进度一目了然,及时调整学习计划,查疑补漏 •人性化 -界面设计简洁,操作简单,没有繁琐的按钮,一点就可直接进入学习状态 -贴心小提示,读懂你的小心思,零基础快速入门,学习无压力,不尴尬 -超多原理解析,疑问解答,如同专业Excel导师亲身指导,不翻书快速记忆,Excel小白迅速成长 •实用性 -课程由浅入深,操作过程详细剖析,一步步模仿,马上就会做。 -涉及排序、筛选、函数公式、数据透视表、图表、汇总等核心功能的课程编排,通俗易懂不头疼 -互动+观看视频,两种学习方式切换,上班路上,地铁里享受随时随地自由学习 通过本课程的学习,您将逐步掌握Excel 2016的各项功能和使用技巧。从而提高您的办公效率,早做,不加班!!!
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页