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

news2024/11/16 19:32:20

** >> 相对于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);
        }

    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[HTML]Web前端开发技术12(HTML5、CSS3、JavaScript )——喵喵画网页

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;佬佬会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

多商户入驻系统APP源码系统:功能强大+分销+秒杀+拼团+砍价+优惠券+完整的安装代码包以及搭建教程

科技的不断发展&#xff0c;互联网在不断的进步&#xff0c;传统的商业形态正在逐步向数字化转型。在这个大背景下&#xff0c;多商户入驻系统APP源码系统应运而生&#xff0c;旨在为各类商家提供一个功能强大的线上商业平台&#xff0c;以提升其市场竞争力。该系统集成了丰富的…

Linux:NTP校时、PTP校时

目录 前言一、NTP校时1、简介2、ubuntu使用 NTP3、嵌入式设备使用 NTP 校时4、NTP 服务器的校时精度 二、PTP校时1、简介2、ubuntu使用 PTP3、嵌入式设备使用 PTP 校时 三、PTP 校时和 NTP 校时那个精度高一些 前言 在进行网络协议通信时&#xff0c;我们有时候需要计算通信的延…

【面试合集】说说提高微信小程序的应用速度的手段有哪些?

面试官&#xff1a;说说提高微信小程序的应用速度的手段有哪些&#xff1f; 一、是什么 小程序启动会常常遇到如下图场景&#xff1a; 这是因为&#xff0c;小程序首次启动前&#xff0c;微信会在小程序启动前为小程序准备好通用的运行环境&#xff0c;如运行中的线程和一些基…

Spring集成

目录 概述1 声朋一个简单的集成流1.1 使用XML定义集成流1.2 使用Java配置集成流1.3 使用Spring lntegration 的 DSL 配置 2 Spring integration 功能概览2.1 消息通道2.2 过滤器2.3 转换器2.4 路由器2.5 切分器2.6 服务激活器2.7 网关2.8 通道适配器2.9 端点模块 概述 就像我们…

图像识别与计算机视觉有什么区别?

图像识别和计算机视觉在很多方面存在差异&#xff0c;这些差异主要体现在以下几个方面&#xff1a; 1. 研究范围 图像识别是计算机视觉领域的一个子集。计算机视觉不仅包括图像识别&#xff0c;还涵盖了更广泛的内容&#xff0c;如场景理解、目标跟踪、分割、识别和解释等。简而…

说清楚Kubernetes、Docker、Dockershim、Containerd、runC、CRI、OCI的关系

Kubernetes v1.20版本 的 release note 里说 deprecated docker。并且在后续版本 v1.24 正式删除了 dockershim 组件&#xff0c;这对我们有什么影响呢&#xff1f;Kubernetes 1.20: The Raddest Release | Kubernetes 为了搞明白这件事情&#xff0c;以及理解一系列容器名词 …

DC电源模块与AC电源模块的对比分析

DC电源模块与AC电源模块的对比分析 BOSHIDA DC电源模块和AC电源模块是两种常见的电源模块&#xff0c;它们在供电方式、稳定性、适用范围等方面有所不同&#xff0c;下面是它们的对比分析&#xff1a; 1. 供电方式&#xff1a; DC电源模块通过直流电源供电&#xff0c;通常使用…

Java里解压zip和rar包

zip的解压提供了一种方法&#xff0c; rar的解压提供了两种方法&#xff0c;第一种方法是调用命令调用主机安装的解压缩工具&#xff0c; 第二种方法&#xff0c;需要注意一下&#xff0c;需要导一个包 <dependency><groupId>com.github.junrar</groupId>&l…

NR C-DRX inactivity Timer的工作原理

drx-inactivityTimer 是C-DRX中比较关键的一个timer&#xff0c;这里是其工作流程的总结。 inactivity-timer是UE等待成功解码PDCCH的持续时间&#xff0c;从PDCCH的最后一次成功解码开启&#xff0c;timer超时后UE可以返回sleep。 UE 应在一次成功解码PDCCH 后重新启动inactiv…

优优嗨聚集团:债务逾期,如何应对与解决?

在现代社会&#xff0c;债务问题已成为越来越多人面临的难题。债务逾期不仅会给个人带来巨大的经济压力&#xff0c;还会影响个人信用记录&#xff0c;甚至可能引发法律纠纷。那么&#xff0c;当债务逾期时&#xff0c;我们应该如何应对与解决呢&#xff1f; 一、了解债务情况 …

数据库——DAY1(Linux上安装MySQL8.0.35(网络仓库安装))

一、环境部署 1、Red Hat Enterprise Linux 9.3 64 位 2、删除之前安装过本地镜像版本的MySQL软件&#xff08;以前未安装过&#xff0c;请跳过此步骤&#xff09; [rootlocalhost ~]# dnf remove mysql-server -y [rootlocalhost ~]# rm -rf /var/lib/mysql [rootlocalhost …

带你了解烧结钕铁硼的成型工艺

与传统的粉末冶金工艺相比&#xff0c;钕铁硼的成型具有磁场取向和氧化防护这两大特点&#xff0c;成型过程基本决定了磁体的几何形状、尺寸和取向度&#xff0c;是烧结钕铁硼制备的关键环节&#xff0c;成型一般分为干压和湿压两大类。 图片来源&#xff1a;曹帅&#xff0c;烧…

梦回2004!我用全志V3s做了个成本100元,功能媲美MP4的随身终端

本项目是基于全志V3S的随身终端&#xff08;类似MP4&#xff09;&#xff0c;命名为V3S-PI&#xff0c;开发板使用四层板制作&#xff0c;全板采用0603电容电阻&#xff0c;相较于0402&#xff0c;制作更为方便&#xff0c;同时成本可压缩至100以内。 项目简介 开发板选用全志…

Linux多网卡绑定实现负载均衡详解

将多块网卡绑定同一IP地址对外提供服务&#xff0c;可以实现高可用或者负载均衡。直接给两块网卡设置同一IP地址是不可以的。通过 bonding&#xff0c;虚拟一块网卡对外提供连接&#xff0c;物理网卡的被修改为相同的MAC地址。 目录 1、bond的作用 2、Bonding聚合链路工作模…

pyqt5 pyinstaller 打包 QThread QLable QscrollArea 滑动 红果短剧

废话 不多说&#xff0c;直接上代码&#xff01;&#xff01;&#xff01; UI.py self.scrollArea QtWidgets.QScrollArea(self.centralwidget)self.scrollArea.setGeometry(QtCore.QRect(20, 130, 541, 511))self.scrollArea.setWidgetResizable(True)self.scrollArea.setOb…

idea2018导入多个javaweb项目-学习笔记

多个javaweb项目导入 file-> Project Structure 确认&#xff0c;test项目的依赖jar包&#xff0c;引入tomcat jar包。 到此导入javaweb并配置完成 </article>

延迟减少10倍!OCD:基于以目标为中心Diffusion的高效视频编辑方法

基于扩散的视频编辑已经取得了令人瞩目的质量&#xff0c;可以根据文本编辑提示转换给定视频输入的全局风格、局部结构和属性。然而&#xff0c;这类解决方案通常需要大量的内存和计算成本来生成时间上连贯的帧&#xff0c;无论是以扩散反演还是跨帧注意力的形式。在本文中&…

MtimeMtimecmp

Mtime: 实时time计数器&#xff0c;可读可写&#xff1b;mtime必须按照一个固定的频率递增&#xff1b;如果count overflow了&#xff0c;则mtime的值需要卷绕&#xff1b;对于32/64的系统来说&#xff0c;mtime的值都是64bits的&#xff1b; 与mtime对应的&#xff0c;还有一…

uniap vue3 组件使用uni.createSelectorQuery() 获取dom报错

由于vue3中没有this&#xff0c;所以使用uni.createSelectorQuery().in(this)时&#xff0c;会报错 使用 getCurrentInstance 获取组件实例 使用 uni.createSelectorQuery() 批量查询时&#xff0c;结果是按照查询的顺序返回的 使用示例 import { getCurrentInstance } from…