Java 树形结构数据生成导出excel文件V2

** >> 相对于V1版本,优化了代码逻辑,合理使用递归计算树数据的坐标 << **

1、效果

在这里插入图片描述

2、使用方法


import com.alibaba.fastjson.JSONArray;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class Tree2ExcelDemo {

    public static void main(String[] args) throws IOException {
        String jsonStr = "[{\"name\":\"aaa\",\"children\":[{\"name\":\"bbb\",\"children\":[{\"name\":\"eee\"},{\"name\":\"fff\",\"children\":[{\"name\":\"iii\"},{\"name\":\"jjj\",\"children\":[{\"name\":\"qqq\"},{\"name\":\"ttt\"}]}]},{\"name\":\"www\"}]},{\"name\":\"ccc\",\"children\":[{\"name\":\"ggg\"},{\"name\":\"hhh\",\"children\":[{\"name\":\"kkk\",\"children\":[{\"name\":\"ttt\"},{\"name\":\"mmm\"}]},{\"name\":\"uuu\"}]},{\"name\":\"ooo\"}]},{\"name\":\"ddd\",\"children\":[{\"name\":\"ggg\"},{\"name\":\"hhh\",\"children\":[{\"name\":\"kkk\"},{\"name\":\"uuu\"}]}]}]}]";

        List<TestDemo.TreeE> list = JSONArray.parseArray(jsonStr, TestDemo.TreeE.class);

        String path = "C:\\Users\\LZY\\Desktop\\" + System.currentTimeMillis() + ".xls";

        File file = new File(path);
        file.createNewFile();

        Workbook workbook = new HSSFWorkbook();

        Tree2ExcelUtil.handle(workbook, list, "TOP", Tree2ExcelUtil.Type.TOP,
                null, null, 1, 1, 1);
        Tree2ExcelUtil.handle(workbook, list, "MIDDLE", Tree2ExcelUtil.Type.MIDDLE,
                null, null, 1, 1, 1);
        Tree2ExcelUtil.handle(workbook, list, "BOTTOM", Tree2ExcelUtil.Type.BOTTOM,
                null, null, 1, 1, 1);

        try (
                FileOutputStream fos = new FileOutputStream(file)
        ) {
            workbook.write(fos);

            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workbook.close();
        }

    }
}

3、源码


import org.apache.poi.ss.usermodel.*;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 树形结构数据导出生成excel文件
 * <p>
 * Created by lzy on 2024/1/13 14:09
 */

@SuppressWarnings("unchecked")
public class Tree2ExcelUtil {

    /**
     * 处理填充 树数据 到工作簿
     *
     * @param workbook 工作簿
     * @param treeList 树数据,children结构
     * @param <T>      数据泛型
     */
    public static <T> void handle(Workbook workbook, List<T> treeList) {
        handle(workbook, treeList, null, Type.MIDDLE, null, null, 1, 1, 1);
    }

    /**
     * 处理填充 树数据 到工作簿
     *
     * @param workbook 工作簿
     * @param treeList 树数据,children结构
     * @param type     树类型
     * @param <T>      数据泛型
     */
    public static <T> void handle(Workbook workbook, List<T> treeList, Type type) {
        handle(workbook, treeList, null, type, null, null, 1, 1, 1);
    }

    /**
     * 处理填充 树数据 到工作簿
     *
     * @param workbook  工作簿
     * @param treeList  树数据,children结构
     * @param sheetName 工作表名,用于给指定工作表填充树数据
     * @param type      树类型
     * @param <T>       数据泛型
     */
    public static <T> void handle(Workbook workbook, List<T> treeList, String sheetName, Type type) {
        handle(workbook, treeList, sheetName, type, null, null, 1, 1, 1);
    }

    /**
     * 处理填充 树数据 到工作簿
     *
     * @param workbook      工作簿
     * @param treeList      树数据,children结构
     * @param type          树类型
     * @param lableField    标签字段名
     * @param childrenField 孩子集合字段名
     * @param <T>           数据泛型
     */
    public static <T> void handle(Workbook workbook, List<T> treeList, Type type, String lableField, String childrenField) {
        handle(workbook, treeList, null, type, lableField, childrenField, 1, 1, 1);
    }

    /**
     * 处理填充 树数据 到工作簿
     *
     * @param workbook      工作簿
     * @param treeList      树数据,children结构
     * @param sheetName     工作表名,用于给指定工作表填充树数据
     * @param type          树类型
     * @param lableField    标签字段名
     * @param childrenField 孩子集合字段名
     * @param <T>           数据泛型
     */
    public static <T> void handle(Workbook workbook, List<T> treeList, String sheetName, Type type,
                                  String lableField, String childrenField) {
        handle(workbook, treeList, sheetName, type, lableField, childrenField, 1, 1, 1);
    }

    /**
     * 处理填充 树数据 到工作簿
     *
     * @param workbook      工作簿
     * @param treeList      树数据,children结构
     * @param sheetName     工作表名,用于给指定工作表填充树数据
     * @param type          树类型
     * @param lableField    标签字段名
     * @param childrenField 孩子集合字段名
     * @param startRow      开始行数,默认:1
     * @param rowOffset     间隔行数,默认:1
     * @param startCol      开始列数,默认:1
     * @param <T>           数据泛型
     */
    public static <T> void handle(Workbook workbook, List<T> treeList,
                                  String sheetName, Type type,
                                  String lableField, String childrenField,
                                  int startRow, int rowOffset, int startCol) {

        if (startRow < 0) {
            throw new Tree2ExcelException("开始行数不能小于0");
        }
        if (rowOffset < 0) {
            throw new Tree2ExcelException("间隔行数不能小于0");
        }
        if (startCol < 1) {
            throw new Tree2ExcelException("开始列数不能小于1");
        }

        List<Map<String, Object>> newList = new ArrayList<>();
        childrenField = emptyToDefault(childrenField, CHILDREN_FIELD);
        handleCoord(treeList, childrenField, startRow, rowOffset, startCol, newList);

        //配置单元格背景色
        CellStyle style1 = getCellStyle(workbook, IndexedColors.LIGHT_GREEN);
        CellStyle style2 = getCellStyle(workbook, IndexedColors.LIGHT_YELLOW);

        sheetName = emptyToDefault(sheetName, "Sheet1");
        Sheet sheet = workbook.getSheet(sheetName);
        if (sheet == null) {
            sheet = workbook.createSheet(sheetName);
        }

        heandleExcel(sheet, type, newList,
                emptyToDefault(lableField, LABLE_FIELD),
                childrenField, style1, style2);
    }

    /*
    列字段标识
     */
    private static final String COL = "_$col";
    /*
    开始行字段标识
     */
    private static final String S_ROW = "_$s_row";
    /*
    中间行字段标识
     */
    private static final String C_ROW = "_$c_row";
    /*
    结束行字段标识
     */
    private static final String E_ROW = "_$e_row";
    /*
    标签字段标识
     */
    private static final String LABLE_FIELD = "name";
    /*
    子集字段标识
     */
    private static final String CHILDREN_FIELD = "children";

    /**
     * 处理树数据坐标
     *
     * @param treeList      树数据,children结构
     * @param childrenField 孩子集合字段名
     * @param startRow      开始行数
     * @param rowOffset     间隔行数
     * @param startCol      开始列数
     * @param result        引用传递
     * @param <T>           数据泛型
     * @return 子集的行高
     */
    private static <T> int handleCoord(List<T> treeList, String childrenField,
                                       int startRow, int rowOffset, int startCol,
                                       List<Map<String, Object>> result) {
        if (isCollNotEmpty(treeList)) {
            // 当前所在元素高度
            int tempRow = 0;
            // 所有子集的高度和
            int childrenRows = 0;
            for (int i = 0; i < treeList.size(); i++) {
                Map<String, Object> item = convertBeanToMap(treeList.get(i));

                tempRow = i + i * rowOffset;
                item.put(COL, startCol);
                item.put(S_ROW, startRow + tempRow + childrenRows);

                // 子集合的高度
                int childRow = 0;
                List<T> children = (List<T>) item.get(childrenField);
                if (isCollNotEmpty(children)) {
                    List<Map<String, Object>> childrenRes = new ArrayList<>();
                    item.put(childrenField, childrenRes);
                    childRow = handleCoord(children, childrenField, startRow + tempRow + childrenRows,
                            rowOffset, startCol + 2, childrenRes);
                }

                item.put(C_ROW, startRow + tempRow + childrenRows + (Math.floorDiv(childRow, 2)));
                childrenRows += childRow;
                item.put(E_ROW, startRow + tempRow + childrenRows);

                result.add(item);
            }
            return tempRow + childrenRows;
        }
        return 0;
    }

    /**
     * 处理Excel
     *
     * @param sheet         工作表
     * @param type          树类型
     * @param list          带有坐标的数据
     * @param lableField    标签字段名
     * @param childrenField 孩子集合字段名
     * @param style1        文本样式
     * @param style2        连线样式
     */
    private static void heandleExcel(Sheet sheet, Type type, List<Map<String, Object>> list,
                                     String lableField, String childrenField,
                                     CellStyle style1, CellStyle style2) {

        if (isCollNotEmpty(list)) {
            int startRow = -1, endRow = -1, fullCol = -1;

            int size = list.size();
            for (int i = 0; i < size; i++) {
                Map<String, Object> item = list.get(i);
                int x = toInt(item.get(COL));
                int y = toInt(item.get(TYPE_KEY_MAP.get(type)));

                Cell cell = getOrCreateCell(sheet, x, y);
                if (cell != null) {
                    cell.setCellStyle(style1);
                    cell.setCellValue(toStringOrNull(item.get(lableField)));
                }

                List<Map<String, Object>> children = (List<Map<String, Object>>) item.get(childrenField);
                if (isCollNotEmpty(children)) {
                    heandleExcel(sheet, type, children, lableField, childrenField, style1, style2);
                }

                if (i == 0) {
                    startRow = y;
                }
                if (i == size - 1) {
                    endRow = y;
                }
                fullCol = x;
            }

            if (startRow != -1 && endRow != -1 && fullCol > 0) {
                sheet.setColumnWidth(fullCol, 256 * 20);
                sheet.setColumnWidth(fullCol - 1, 256);
                for (; startRow <= endRow; startRow++) {
                    Cell cell = getOrCreateCell(sheet, fullCol - 1, startRow);
                    if (cell != null) {
                        cell.setCellStyle(style2);
                    }
                }
            }
        }

    }


    /**
     * 获取或创建 Cell
     *
     * @param sheet 工作表
     * @param x     列
     * @param y     行
     * @return Cell
     */
    private static Cell getOrCreateCell(Sheet sheet, int x, int y) {
        Row row = sheet.getRow(y);
        if (row == null) {
            row = sheet.createRow(y);
        }
        Cell cell = row.getCell(x);
        if (cell == null) {
            cell = row.createCell(x);
        }
        return cell;
    }


    /**
     * 获取Cell样式
     *
     * @param workbook      工作簿
     * @param indexedColors 颜色
     * @return 样式
     */
    private static CellStyle getCellStyle(Workbook workbook, IndexedColors indexedColors) {
        CellStyle style = workbook.createCellStyle();
        style.setFillForegroundColor(indexedColors.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return style;
    }

    /**
     * 转整型
     *
     * @param val 值
     * @return 整型值
     */
    private static int toInt(Object val) {
        try {
            return Integer.parseInt(String.valueOf(val));
        } catch (NumberFormatException ignored) {
        }
        return 0;
    }

    /**
     * 转字符串
     *
     * @param obj 值
     * @return 字符串值
     */
    public static String toStringOrNull(Object obj) {
        return null == obj ? null : obj.toString();
    }

    /**
     * 字符串若空则默认
     *
     * @param str        字符串
     * @param defaultStr 默认值
     * @return 字符串
     */
    public static String emptyToDefault(CharSequence str, String defaultStr) {
        return str == null || str.length() == 0 ? defaultStr : str.toString();
    }

    /**
     * 集合是否为空
     *
     * @param collection 集合
     * @return 是否为空
     */
    private static boolean isCollNotEmpty(Collection<?> collection) {
        return !(collection == null || collection.isEmpty());
    }

    /**
     * 实体类转Map
     *
     * @param bean 实体类
     * @return Map值
     */
    public static Map<String, Object> convertBeanToMap(Object bean) {
        if (bean instanceof List || bean instanceof Set) {
            throw new Tree2ExcelException("传递的元素值不符合要求");
        }
        if (bean instanceof Map) {
            return (Map<String, Object>) bean;
        }
        Map<String, Object> map = new HashMap<>();
        try {
            Class<?> clazz = bean.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(bean));
            }
        } catch (IllegalAccessException ignored) {
        }
        return map;
    }

    private static final Map<Type, String> TYPE_KEY_MAP = new HashMap<>();

    static {
        TYPE_KEY_MAP.put(Type.TOP, S_ROW);
        TYPE_KEY_MAP.put(Type.MIDDLE, C_ROW);
        TYPE_KEY_MAP.put(Type.BOTTOM, E_ROW);
    }

    public static enum Type {
        /**
         * 上对齐
         */
        TOP,
        /**
         * 中对齐
         */
        MIDDLE,
        /**
         * 下对齐
         */
        BOTTOM;
    }

    @SuppressWarnings("unused")
    public static class Tree2ExcelException extends RuntimeException {
        public Tree2ExcelException() {
            super();
        }

        public Tree2ExcelException(String message) {
            super(message);
        }

        public Tree2ExcelException(String message, Throwable cause) {
            super(message, cause);
        }

        public Tree2ExcelException(Throwable cause) {
            super(cause);
        }

    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/320314.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Shiro框架:Shiro登录认证流程源码解析

目录 1.用户登录认证流程 1.1 生成认证Token 1.2 用户登录认证 1.2.1 SecurityManager login流程解析 1.2.1.1 authenticate方法进行登录认证 1.2.1.1.1 单Realm认证 1.2.1.2 认证通过后创建登录用户对象 1.2.1.2.1 复制SubjectContext 1.2.1.2.2 对subjectContext设…

【如何在 GitHub上面找项目】【转载】

很多的小伙伴&#xff0c;经常会有这样的困惑&#xff0c;我看了很多技术的学习文档、书籍、甚至视频&#xff0c;我想动手实践&#xff0c;于是我打开了GitHub&#xff0c;想找个开源项目&#xff0c;进行学习&#xff0c;获取项目实战经验。这个时候很多小伙伴就会面临这样的…

【数据结构 | 直接选择排序】

直接选择排序 基本思路直接插入排序SelectSort 基本思路 直接插入排序&#xff08;StraightInsertionSort&#xff09;的基本操作是将一个记录插入到已经排好序的有序表中&#xff0c;从而得到一个新的、记录数增1的有序表。 我们可以同时从数组的头部和尾部同时进行排序工作…

Pandoc:markdown转word

简介&#xff1a;Pandoc是由John MacFarlane开发的标记语言转换工具&#xff0c;可实现不同标记语言间的格式转换&#xff0c;堪称该领域中的“瑞士军刀”。Pandoc使用Haskell语言编写&#xff0c;以命令行形式实现与用户的交互&#xff0c;可支持多种操作系统&#xff1b;Pand…

IP-Adapter:用于文本到图像扩散模型的文本兼容图像提示适配器

文章目录 一、IP-Adapter简介二、IP-Adapter与img2img的区分&#xff08;一&#xff09;结构上的区别&#xff08;二&#xff09;流程上的区别&#xff08;三&#xff09;输出上的区别&#xff08;四&#xff09;原理上的区别 三、IP-Adapter的网络架构&#xff08;一&#xff…

自定义C#类库(.dll文件)

环境配置 操作系统&#xff1a;Windows 10 开发工具&#xff1a;Visual Studio 2022 .Net桌面开发环境&#xff1a; 开发步骤 &#xff08;一&#xff09;创建C#类库项目 &#xff08;二&#xff09;配置项目名称和项目路径 &#xff08;三&#xff09;选择所使用的框架&a…

ES数据聚合

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

PDF 文档解除密码

PDF 文档解除密码 1. 文件 -> 文档属性 -> 安全 -> 文档限制摘要2. PDF365References 1. 文件 -> 文档属性 -> 安全 -> 文档限制摘要 密码保护《算法设计与分析基础_第3版.pdf》 2. PDF365 https://www.pdf365.cn/ 免费功能 -> PDF 去密码 开始去除 Re…

PVE虚拟机配置文件恢复

一、pve 创建的虚拟机的配置文件位置 在宿主机的 /etc/pve/qemu-server&#xff0c;这里有创建虚拟机的相关硬件信息。 rootpve1:/etc/pve/qemu-server# pwd /etc/pve/qemu-server二、故障现象 在命令行执行qm list不显示虚拟机&#xff0c;查看 宿主机的 /etc/pve/qemu-ser…

【算法】Java-二叉树的右视图(BFS、DFS两种解法)

题目要求&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4]示例 2: 输入: [1,null,3] 输出: [1,3]示例 3: 输入…

mysql原理--undo日志2

1.概述 上一章我们主要唠叨了为什么需要 undo日志 &#xff0c;以及 INSERT 、 DELETE 、 UPDATE 这些会对数据做改动的语句都会产生什么类型的 undo日志 &#xff0c;还有不同类型的 undo日志 的具体格式是什么。本章会继续唠叨这些 undo日志 会被具体写到什么地方&#xff0c…

LabVIEW利用视频分析实现高效硬度测量

LabVIEW利用视频分析实现高效硬度测量 在材料硬度测量领域&#xff0c;自动化和高精度测试技术的需求不断上升。布氏硬度机的自动化测量系统&#xff0c;尤其是那些结合了LabVIEW视频识别和处理技术的系统&#xff0c;正日益成为行业的焦点。介绍一个使用LabVIEW软件和先进的视…

mysql-实战案例 (超详细版)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

用通俗易懂的方式讲解:大模型 RAG 技术,从入门到精通

本文基于IVAN ILIN发布于Towards AI的博客[1]进行总结归纳&#xff0c;感谢原作者的精彩讲解。 检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;简称RAG&#xff09;为大型语言模型&#xff08;LLMs&#xff09;提供了从某些数据源检索到的信息&#xff0…

svn spring项目增量打包工具

svn spring项目增量打包工具 前提介绍 项目使用svn &#xff0c;打包方式为war包&#xff0c;开发工具ide 项目有时候更新功能只需要更新部分class和html文件&#xff0c;但是要每个都打包并不是很简单 听说idea有现成的插件可以实现这个功能&#xff0c;但是我没找到&…

PPT插件-大珩助手-保留原素材的位置和大小一键替换

保留原素材的位置和大小一键替换 若勾选了一键替换&#xff0c;对于从素材库插入的图形&#xff0c;可以使得它的位置、大小与幻灯片中选中的形状一致 软件介绍 PPT大珩助手是一款全新设计的Office PPT插件&#xff0c;它是一款功能强大且实用的PPT辅助工具&#xff0c;支持W…

软件测试|QtDesigner配置以及使用

简介 上一篇文章我们介绍了PyQt5环境的安装和配置&#xff0c;并且安装了Qt tools工具&#xff0c;本文我们将介绍如何使用Qt tools的QtDesigner如何使用。 QtDesigner 的启动和入门 打开我们的项目从顶部菜单栏选择&#xff1a;Tools -> ExternalTools -> QtDesigner…

电脑重置网络后连不上网了怎么办

一般电脑重置网络后都会自动重新下载好网络配置&#xff0c;但是不免会出现一些意外&#xff0c;接下来就我遇到的重置后无法联网的解决方案 做一个分享&#xff1a; 1、按下“winR”打开运行输入 services.msc 。 2、找到 WLAN AutoConfig 和 Wired AutoConfig 服务&#xff…

class_3:lambda表达式

1、lambda表达式是c11引入的一种匿名函数的方式&#xff0c;它允许你在需要函数的地方内联的定义函数&#xff0c;而无需单独命名函数&#xff1b; #include <iostream>using namespace std;bool compare(int a,int b) {return a > b; }int getMax(int a,int b,bool (…

跟着cherno手搓游戏引擎【6】ImGui

导入ImGui&#xff1a; 下载链接&#xff1a; GitHub - TheCherno/imgui: Dear ImGui: Bloat-free Immediate Mode Graphical User interface for C with minimal dependencies 新建文件夹&#xff0c;把下载好的文件放入对应路径&#xff1a; SRC下的premake5.lua文件&#…