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

好文章原文复制(我只是传播者),直接去看原文(我至今还不会使用,写死的实现了,先记录耻辱list转成tree参照原文连接写死的)原文链接

一、需求

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

图1-1

二、代码实现

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

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

id(主键id)parentid(父节点id)
1null
21
31
41
51
61
72
82
93
104
117
125
1310
148
1511
1612
1713

这里,如何把数据存储成这样的数据存储到数据库中去我就不过多的描述,只要有一定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数据项中,可以看出返回的是多叉树数据结构,由于数据过多我就不全部展示了
3-1

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值