JAVA POI 把execl树形结构转成多叉树结构返回JSON字符串给前端

一、需求

        因为在做项目的过程中,需要把图1-1的execl表格的表头数据转成多叉树结构数据。其中图中execl表头的数据的排列方式完全严格按多叉树的结构进行排列的,项目需求的目标就是把这种多叉树的表头解析成java中的多叉树结构并形成json字符串返回给前端。

                                                                                                                                                            图1-1

二、代码实现

步骤一:poi工具解析execl表格数据,并把解析的数据存储到数据结构中,如图2-1所示

                                                 

                                                                                                                                       图2-1

简单的说,我们就是要把我们解析的execl中的数据,形成表2-2这种有层级关联的数据,其中最顶级的结点的父结点的parentid为null。

                          id(主键id)               parentid(父节点id)
                          1                       null
                          2                        1
                          3                        1
                          4                        1
                          5                        1
                          6                        1
                          7                        2
                          8                        2
                          9                        3
                         10                        4
                         11                        7
                         12                        5
                         13                       10
                         14                        8
                         15                       11
                         16                       12
                         17                       13

                                                                                                                               表2-2

这里,如何把数据存储成这样的数据存储到数据库中去我就不过多的描述,只要有一定java基础的程序员应该都会,下面我们描述根据这样的数据形成多叉树

结构的json数据返回给前端。

步骤二:

好了,开门见山,先上代码。代码中注释非常清晰,几乎都有相关的注释,所以以下代码不做过多的解析。

package org.jeecg.modules.powergrid.templatemanagement.common;

import com.alibaba.fastjson.JSON;
import org.jeecg.modules.powergrid.templatemanagement.common.interfaces.ITree;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;

public class TreeUtils {

    /**
     * 集合转树结构
     * @param collection 目标集合
     * @param clazz 集合元素类型
     * @param <T>
     * @return
     */
    public static <T> Collection<T> toTree(@NotNull Collection<T> collection,@NotNull Class<T> clazz){
        return toTree(collection,null,null,null,clazz);
    }

    /**
     * 集合转树结构,注意,使用此方法,则集合元素必须继承ITree接口
     * @param collection 目标集合
     * @param <T>
     * @return
     */
    public static <T extends ITree> Collection<T> toTree(@NotNull Collection<T> collection){
        try {
            if (collection==null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
            // 找出所有的根节点
            Collection<T> roots = null;
            if (collection.getClass().isAssignableFrom(Set.class)) roots = new HashSet<>();
            else roots = new ArrayList<>();
            for (T tree:collection){
                Object o = ITree.class.getMethod("getParentLevel").invoke(tree);
                if (o instanceof String){
                    if (StringUtil.isEmpty((String) o)){
                        roots.add(tree);
                    }
                }else if (o == null){
                    roots.add(tree);
                }
            }
            // 从目标集合移除所有的根节点
            collection.removeAll(roots);
            // 为根节点添加孩子节点
            for (T tree:roots){
                addChild(tree,collection);
            }
            return roots;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 集合转树结构
     * @param collection 目标集合
     * @param id 被依赖字段名称
     * @param parent 依赖字段名称
     * @param children 子节点集合属性名称
     * @param clazz 集合元素类型
     * @param <T>
     * @return
     */
    public static  <T> Collection<T> toTree(@NotNull Collection<T> collection,String id,String parent,String children,@NotNull Class<T> clazz){
        try {
            if (collection==null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
            if (StringUtil.isEmpty(id)) id = "headid";// 如果被依赖字段名称为空则默认为id
            if (StringUtil.isEmpty(parent)) parent = "parentLevel";// 如果依赖字段为空则默认为parent
            if (StringUtil.isEmpty(children)) children = "children";// 如果子节点集合属性名称为空则默认为children
            Collection<T> roots = null;// 初始化根节点集合
            if (collection.getClass().isAssignableFrom(Set.class)) roots = new HashSet<>();// 如果目标节点是一个set集合,则初始化根节点集合为hashset
            else roots = new ArrayList<>();// 否则初始化为Arraylist,
            // 这里集合初始化只分2中,要么是hashset,要么ArrayList,因为这两种最常用,其他不常用的摒弃
            Field idField = null;
            try {
                idField=clazz.getDeclaredField(id);// 获取依赖字段
            }catch (NoSuchFieldException e1){
                idField=clazz.getSuperclass().getDeclaredField(id);
            }
            Field parentField = null;
            try {
                parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
            }catch (NoSuchFieldException e1){
                parentField = clazz.getSuperclass().getDeclaredField(parent);
            }
            Field childrenField = null;// 获取孩子字段
            try {
                childrenField=clazz.getDeclaredField(children);
            }catch (NoSuchFieldException e1){
                childrenField=clazz.getSuperclass().getDeclaredField(children);
            }
            // 设置为可访问
            idField.setAccessible(true);
            parentField.setAccessible(true);
            childrenField.setAccessible(true);
            // 找出所有的根节点
            for (T c:collection){
                Object o = parentField.get(c);
                if (o instanceof String){
                    if (StringUtil.isEmpty((String) o)) {// 如果父节点为空则说明是根节点,添加到根节点集合
                        roots.add(c);
                    }
                }else {
                    if (o==null){
                        roots.add(c);
                    }
                }
            }
            // 从目标集合移除所有根节点
            collection.removeAll(roots);
            for (T c:roots){// 遍历根节点,依次添加子节点
                addChild(c,collection,idField,parentField,childrenField);
            }
            // 关闭可访问
            idField.setAccessible(false);
            parentField.setAccessible(false);
            childrenField.setAccessible(false);
            return roots;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static <T extends ITree> void addChild(T tree,Collection<T> collection){
        try {
            Object id = ITree.class.getMethod("getHeadid").invoke(tree);
            Collection<T> children = (Collection<T>) ITree.class.getMethod("getChildren").invoke(tree);
            for (T cc:collection){
                Object o = ITree.class.getMethod("getParentLevel").invoke(cc);
                if (id.equals(o)){// 如果当前节点的被依赖值和目标节点的被依赖值相等,则说明,当前节点是目标节点的子节点
                    if (children==null) {// 如果目标节点的孩子集合为null,初始化目标节点的孩子集合
                        if (collection.getClass().isAssignableFrom(Set.class)){// 如果目标集合是一个set集合,则初始化目标节点的孩子节点集合为set
                            children = new HashSet<>();
                        }else children = new ArrayList<>();// 否则初始化为list
                    }
                    // 将当前节点添加到目标节点的孩子节点
                    children.add(cc);
                    // 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
                    ITree.class.getMethod("setChildren", Collection.class).invoke(tree,children);
                    // 递归添加孩子节点
                    addChild(cc,collection);
                }
            }
        } catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 为目标节点添加孩子节点,此方法为私有,不能为公开,否则类修改信息无法恢复,后面有公开方法,其专门为目标节点添加子节点
     * @param c 目标节点
     * @param collection 目标集合
     * @param idField
     * @param parentField
     * @param childrenField
     * @param <T>
     * @throws IllegalAccessException
     */
    private static <T> void addChild(@NotNull T c,@NotNull Collection<T> collection,@NotNull Field idField,@NotNull Field parentField,@NotNull Field childrenField) throws IllegalAccessException {
        Object id =  idField.get(c);// 获取目标节点的被依赖值
        Collection<T> children = (Collection<T>) childrenField.get(c);// 获取目标节点的孩子列表
        for (T cc:collection){// 遍历目标集合
            Object o = parentField.get(cc);// 获取当前节点的依赖值
            if (id.equals(o)){// 如果当前节点的被依赖值和目标节点的被依赖值相等,则说明,当前节点是目标节点的子节点
                if (children==null) {// 如果目标节点的孩子集合为null,初始化目标节点的孩子集合
                    if (collection.getClass().isAssignableFrom(Set.class)){// 如果目标集合是一个set集合,则初始化目标节点的孩子节点集合为set
                        children = new HashSet<>();
                    }else children = new ArrayList<>();// 否则初始化为list
                }
                // 将当前节点添加到目标节点的孩子节点
                children.add(cc);
                // 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
                childrenField.set(c,children);
                // 递归添加孩子节点
                addChild(cc,collection,idField,parentField,childrenField);
            }
        }
        // 特别说明:大家可以看到此递归没有明显出口,其出口就是是否当前节点的依赖值和目标节点的被依赖值一样,一样就递归,不一样进不了if,自然出递归
        // 此工具类自我感觉是最简单的,最实用的工具类,我看网上许多人写的,都是云的雾的,本来也想借鉴,但是实在没一个能看的感觉思路清晰,没办法,自己动手造轮子
    }

    /**
     * 为目标节点添加孩子
     * @param c 目标节点
     * @param collection 目标集合
     * @param id 被依赖字段名
     * @param parent 依赖字段名
     * @param children 孩子节点字段名
     * @param clazz 集合元素所在类别
     * @param <T>
     */
    public static <T> void addChild(@NotNull T c,@NotNull Collection<T> collection,String id,String parent,String children,@NotNull Class<T> clazz){
        try {
            if (collection==null || collection.isEmpty()) return ;// 如果目标集合为空,直接返回一个空树
            if (StringUtil.isEmpty(id)) id = "headid";// 如果被依赖字段名称为空则默认为id
            if (StringUtil.isEmpty(parent)) parent = "parentLevel";// 如果依赖字段为空则默认为parent
            if (StringUtil.isEmpty(children)) children = "children";// 如果子节点集合属性名称为空则默认为children
            Field idField = null;
            try {
                idField=clazz.getDeclaredField(id);// 获取依赖字段
            }catch (NoSuchFieldException e1){
                idField=clazz.getSuperclass().getDeclaredField(id);
            }
            Field parentField = null;
            try {
                parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
            }catch (NoSuchFieldException e1){
                parentField = clazz.getSuperclass().getDeclaredField(parent);
            }
            Field childrenField = null;// 获取孩子字段
            try {
                childrenField=clazz.getDeclaredField(children);
            }catch (NoSuchFieldException e1){
                childrenField=clazz.getSuperclass().getDeclaredField(children);
            }
            // 设置为可访问
            idField.setAccessible(true);
            parentField.setAccessible(true);
            childrenField.setAccessible(true);
            addChild(c,collection,idField,parentField,childrenField);
            // 关闭可访问
            idField.setAccessible(false);
            parentField.setAccessible(false);
            childrenField.setAccessible(false);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 为目标节点添加孩子
     * @param c 目标节点
     * @param collection 目标集合
     * @param clazz 集合元素所在类型
     * @param <T>
     */
    public static <T> void addChild(@NotNull T c,@NotNull Collection<T> collection,@NotNull Class<T> clazz){
        addChild(c,collection,null,null,null,clazz);
    }


}



package org.jeecg.modules.powergrid.templatemanagement.common.interfaces;


import java.io.Serializable;
import java.util.Collection;
public interface ITree {
    /**
     * 获取被依赖节点
     * @return
     */
    Serializable getHeadid();

    /**
     * 设置被依赖节点
     * @param id
     */
    void setHeadid(Serializable id);

    /**
     * 获取依赖节点
     * @return
     */
    Serializable getParentLevel();

    /**
     * 设置依赖节点
     * @param parent
     */
    void setParentLevel(Serializable parent);

    /**
     * 获取孩子列表
     * @return
     */
    Collection<? extends ITree> getChildren();

    /**
     * 设置孩子列表
     * @param children
     */
    void setChildren(Collection<? extends ITree> children);
}

 

三、代码运行结果(返回的json数据)

如图3-1所示,返回的结果都在dataBasicHeaderList数据项中,可以看出返回的是多叉树数据结构,由于数据过多我就不全部展示了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Jeecg 是一款基于代码生成器和模板引擎的开源快速开发框架,它提供了丰富的代码生成功能,包括导出 Excel 功能。下面是 Jeecg 中导出 Excel 的示例代码: 1. 首先在需要导出 Excel 的 Controller 中注入 ExcelUtil 工具类: ```java @Autowired private ExcelUtil excelUtil; ``` 2. 在需要导出 Excel 的方法中使用 ExcelUtil 工具类生成 Excel 文件: ```java @RequestMapping(value = "/exportExcel") public void exportExcel(HttpServletRequest request, HttpServletResponse response) { // 查询数据 List<MyEntity> dataList = myService.getDataList(); // 定义表头 String[] headers = {"ID", "名称", "时间"}; // 定义数据字段 String[] fields = {"id", "name", "time"}; // 使用 ExcelUtil 工具类生成 Excel 文件 excelUtil.exportExcel("导出数据", headers, fields, dataList, response); } ``` 其中,`MyEntity` 是需要导出的实体类,`getDataList()` 方法是从数据库或其他地方获取需要导出的数据,`headers` 数组存放 Excel 表头,`fields` 数组存放实体类的属性名与表头对应,最后调用 `exportExcel` 方法生成 Excel 文件并返回前端。 3. ExcelUtil 工具类示例代码: ```java public class ExcelUtil { /** * 导出 Excel 文件 * * @param title Excel 标题 * @param headers 表头 * @param fields 数据字段 * @param dataList 数据列表 */ public void exportExcel(String title, String[] headers, String[] fields, List<?> dataList, HttpServletResponse response) { try { // 创建工作簿 HSSFWorkbook workbook = new HSSFWorkbook(); // 创建工作表 HSSFSheet sheet = workbook.createSheet(title); // 创建表头行 HSSFRow headerRow = sheet.createRow(0); // 设置表头单元格样式 HSSFCellStyle headerCellStyle = workbook.createCellStyle(); headerCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex()); headerCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); headerCellStyle.setAlignment(HorizontalAlignment.CENTER); headerCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); headerCellStyle.setBorderTop(BorderStyle.THIN); headerCellStyle.setBorderBottom(BorderStyle.THIN); headerCellStyle.setBorderLeft(BorderStyle.THIN); headerCellStyle.setBorderRight(BorderStyle.THIN); // 创建表头单元格 for (int i = 0; i < headers.length; i++) { HSSFCell headerCell = headerRow.createCell(i); headerCell.setCellValue(headers[i]); headerCell.setCellStyle(headerCellStyle); } // 设置数据单元格样式 HSSFCellStyle dataCellStyle = workbook.createCellStyle(); dataCellStyle.setAlignment(HorizontalAlignment.CENTER); dataCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); dataCellStyle.setBorderTop(BorderStyle.THIN); dataCellStyle.setBorderBottom(BorderStyle.THIN); dataCellStyle.setBorderLeft(BorderStyle.THIN); dataCellStyle.setBorderRight(BorderStyle.THIN); // 创建数据单元格 for (int i = 0; i < dataList.size(); i++) { HSSFRow dataRow = sheet.createRow(i + 1); Object obj = dataList.get(i); for (int j = 0; j < fields.length; j++) { HSSFCell dataCell = dataRow.createCell(j); Field field = obj.getClass().getDeclaredField(fields[j]); field.setAccessible(true); Object value = field.get(obj); if (value instanceof Date) { dataCell.setCellValue((Date) value); } else { dataCell.setCellValue(value.toString()); } dataCell.setCellStyle(dataCellStyle); } } // 设置响应头 response.setContentType("application/octet-stream;charset=utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + new String((title + ".xls").getBytes(), "iso-8859-1")); // 输出 Excel 文件 workbook.write(response.getOutputStream()); response.getOutputStream().flush(); response.getOutputStream().close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 以上就是 Jeecg 中导出 Excel 的示例代码,你可以根据自己的需求进行修改。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wgay520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值