写了一个早上,终于实现,现在分享如下:
所有功能一个TreeNode完成,如下图:
TreeNode代码如下,你不要错过:)
package model;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/** 自定义的树节点 */
public class TreeNode {
// 节点携带的值
private Map tag;
// 儿子节点
private List nodes;
/** 节点信息键值对,该属性不会为空指针,但可能没有键值对存在[就是map.size()==0] */
public Map getTag() {
if (tag == null)
tag = new HashMap();
return tag;
}
public void setTag(Map tag) {
this.tag = tag;
}
/** 获得所有子节点,该属性不会为空指针,但可能没有集合元素存在 */
public List getNodes() {
if (nodes == null)
nodes = new ArrayList();
return nodes;
}
public void setNodes(List nodes) {
this.nodes = nodes;
}
public TreeNode(Map tag, List nodes) {
super();
this.tag = tag;
this.nodes = nodes;
}
public TreeNode() {
super();
}
public TreeNode(Map tag) {
super();
this.tag = tag;
}
/**
* * 将当前TreeNode解析为JSON字串 构造的目标格式示例:
* {
* id:1,
* name:'手机',
* nodes:[
* {id:11,name:'诺基亚'},
* {id:12,name:'三星',nodes:[
* {id:121,name:'I9000(联通版)'},
* {id:122,name:'I9000(移动版)'},
* {id:123,name:'Galaxy Naos'}
* ]},
* {id:13,name:'索爱'}
* ]
* }
*
* */
private String toJson() {
// if (this.getNodes().size() == 0)
// throw new RuntimeException("节点不能没有任何属性");
return parseTreeNode(this);
}
/**
* 将对象集合解析为json格式的树形脚本
* @param 集合成员类型
* @param entities 待解析的集合,必须具备有效值
* @param key 对象的唯一标识属性,必须具备有效值
* @param parentKey 父对象的唯一标识属性,必须具备有效值
* @param fieldPropertyMap java对象属性名到json对象属性名的映射,比如java对象的属性名是code,对应解析后的json对象的属性名是id
* @param parseCallBack 解析回调函数,用于扩展树节点的属性,有了这个接口给开发这个一个可以自定义树形节点特殊属性的机会
* @param parseFields 需要解析的字段,没有传递就解析对象的所有属性
* @return json格式的树形脚本
* 例如结果如下格式:
*
* [
* {id:11,name:'诺基亚'},
* {id:12,name:'三星',nodes:[
* {id:121,name:'I9000(联通版)'},
* {id:122,name:'I9000(移动版)'},
* {id:123,name:'Galaxy Naos'}
* ]},
* {id:13,name:'索爱'},
* {id:14,name:'TCL'},
* {id:15,name:'摩托罗拉'}
* ]
* 使用示范如下:
* * //用一个集合模拟数据表的内容,也就是一下集合的内容完全可以查询自数据库
* List treeList = new ArrayList();
* treeList.add(new SysFunction("L01", null, "营销管理","没有"));
* treeList.add(new SysFunction("L02",null,"客户管理","没有"));
* treeList.add(new SysFunction("L03",null,"服务管理","没有"));
* //营销管理的子功能
* treeList.add(new SysFunction("L0101","L01","创建销售机会","没有"));
* treeList.add(new SysFunction("L0102","L01","指派销售机会","没有"));
* treeList.add(new SysFunction("L0103","L01","制定开发计划","没有"));
* treeList.add(new SysFunction("L0104","L01","执行开发计划","没有"));
* //指派销售机会的子功能
* treeList.add(new SysFunction("L010201","L0102","经理指派","没有"));
* treeList.add(new SysFunction("L010202","L0102","主管指派","没有"));
* //客户管理的子功能
* treeList.add(new SysFunction("L0201","L02","客户信息管理","没有"));
* treeList.add(new SysFunction("L0202","L02","客户流失管理","没有"));
* //服务管理子功能
* treeList.add(new SysFunction("L0301","L03","咨询服务管理","没有"));
* treeList.add(new SysFunction("L0302","L03","投诉服务管理","没有"));
* treeList.add(new SysFunction("L0303","L03","查询服务管理","没有"));
*
* try {
* Properties p = new Properties();
* p.setProperty("code", "id");//将SysFunction对象里的属性名“code”映射到json对象里的属性名“id”
* p.setProperty("title", "name");//将SysFunction对象里的属性名“title”映射到json对象里的属性名“name”
*
* //开始调用解析函数,第五个参数是一个匿名内部类
* return
* TreeNode.parseListToJsonTree(
* treeList,
* "code",
* "parentCode",
* p,
* new TreeNode.IParseTreeNodeCallBack() {
* public void processTreeNode(TreeNode node) {
* node.getTag().put("open",true);//向节点追加一个属性,表示要zTree展开这个节点
* node.getTag().put("click","alert('"+node.getTag().get("id")+"')");//向节点追加一个属性click,表示要求zTree的节点具有响应点击事件的能力
* }
* },
* "code", //java对象SysFunction里要code属性参与解析
* "parentCode", //java对象SysFunction里要parentCode属性参与解析
* "title" //java对象SysFunction里要title属性参与解析
* );
* } catch (Exception e) {
* e.printStackTrace();
* }
*
* @throws SecurityException java反射异常
* @throws NoSuchFieldException java反射异常
* @throws IllegalArgumentException java反射异常
* @throws IllegalAccessException java反射异常
*/
public static String parseListToJsonTree(List entities,
String key, String parentKey, Properties fieldPropertyMap,IParseTreeNodeCallBack parseCallBack,String...parseFields) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
if (entities==null || entities.size()==0 || null==key || "".equals(key) || null==parentKey || "".equals(parentKey))
throw new RuntimeException("参数不是有效值,分析失败");
if (fieldPropertyMap==null)
fieldPropertyMap = new Properties();
List nodes = new ArrayList();
//将这个集合封装成Map
Map treeNodeMap = new HashMap();
Class classzz = entities.get(0).getClass();
Field keyField = classzz.getDeclaredField(key);
Field parentKeyField = classzz.getDeclaredField(parentKey);
keyField.setAccessible(true);//设置可以访问私有字段
parentKeyField.setAccessible(true);//设置可以访问私有字段
String keyJsonPropertyName = "";//key的json属性
String parentKeyJsonPropertyName="";//parentKey的json属性
//处理key
if (fieldPropertyMap.containsKey(key))//如果包含对key的名称映射
keyJsonPropertyName = fieldPropertyMap.getProperty(key);//如果包含key映射就用映射名字作json对象属性名
else
keyJsonPropertyName = keyField.getName();//如果不包含key映射就用字段名作json对象属性名
//同上处理parentKey
if (fieldPropertyMap.containsKey(parentKey))//如果包含对key的名称映射
parentKeyJsonPropertyName = fieldPropertyMap.getProperty(parentKey);
else
parentKeyJsonPropertyName = parentKeyField.getName();
//下面的循环完成两个功能:
// 1.将所有对象封装成Map里面的键值对,且值是TreeNode类型
// //2.将没有父节点的TreeNode添加到根节点rootNode
for(E e:entities){
TreeNode node = new TreeNode();
//处理关键属性
//处理key
node.getTag().put(keyJsonPropertyName, keyField.get(e));
//同上处理parentKey
node.getTag().put(parentKeyJsonPropertyName,parentKeyField.get(e));
//处理其它属性
if (parseFields==null || parseFields.length==0){
for(Field field: classzz.getDeclaredFields()){
field.setAccessible(true);//设置可以访问私有字段
if (fieldPropertyMap.containsKey(field.getName()))//如果map中包含名字映射
node.getTag().put(fieldPropertyMap.getProperty(field.getName()), field.get(e));
else
node.getTag().put(field.getName(),field.get(e));//不包含名字映射则使用字段本身的名字作json对象属性
}
} else {
for(String fieldName: parseFields){
Field field = classzz.getDeclaredField(fieldName);
field.setAccessible(true);//设置可以访问私有字段
if (fieldPropertyMap.containsKey(field.getName()))//如果map中包含名字映射
node.getTag().put(fieldPropertyMap.getProperty(field.getName()), field.get(e));
else
node.getTag().put(field.getName(),field.get(e));//不包含名字映射则使用字段本身的名字作json对象属性
}
}
if (parseCallBack!=null)//判断回调是否为空
parseCallBack.processTreeNode(node);//回调
treeNodeMap.put(keyField.get(e).toString(),node);
}
//下面的代码建立节点之间的从属关系
for(int i=0;i 集合成员类型
* @param entities 待解析的集合,必须具备有效值
* @param key 对象的唯一标识属性,必须具备有效值
* @param parentKey 父对象的唯一标识属性,必须具备有效值
* @param fieldPropertyMap java对象属性名到json对象属性名的映射,比如java对象的属性名是code,对应解析后的json对象的属性名是id
* @return json格式的树形脚本
* 例如结果如下格式:
*
* [
* {id:11,name:'诺基亚'},
* {id:12,name:'三星',nodes:[
* {id:121,name:'I9000(联通版)'},
* {id:122,name:'I9000(移动版)'},
* {id:123,name:'Galaxy Naos'}
* ]},
* {id:13,name:'索爱'},
* {id:14,name:'TCL'},
* {id:15,name:'摩托罗拉'}
* ]
* 使用示范如下:
* * //用一个集合模拟数据表的内容,也就是一下集合的内容完全可以查询自数据库
* List treeList = new ArrayList();
* treeList.add(new SysFunction("L01", null, "营销管理","没有"));
* treeList.add(new SysFunction("L02",null,"客户管理","没有"));
* treeList.add(new SysFunction("L03",null,"服务管理","没有"));
* //营销管理的子功能
* treeList.add(new SysFunction("L0101","L01","创建销售机会","没有"));
* treeList.add(new SysFunction("L0102","L01","指派销售机会","没有"));
* treeList.add(new SysFunction("L0103","L01","制定开发计划","没有"));
* treeList.add(new SysFunction("L0104","L01","执行开发计划","没有"));
* //指派销售机会的子功能
* treeList.add(new SysFunction("L010201","L0102","经理指派","没有"));
* treeList.add(new SysFunction("L010202","L0102","主管指派","没有"));
* //客户管理的子功能
* treeList.add(new SysFunction("L0201","L02","客户信息管理","没有"));
* treeList.add(new SysFunction("L0202","L02","客户流失管理","没有"));
* //服务管理子功能
* treeList.add(new SysFunction("L0301","L03","咨询服务管理","没有"));
* treeList.add(new SysFunction("L0302","L03","投诉服务管理","没有"));
* treeList.add(new SysFunction("L0303","L03","查询服务管理","没有"));
*
* try {
* Properties p = new Properties();
* p.setProperty("code", "id");//将SysFunction对象里的属性名“code”映射到json对象里的属性名“id”
* p.setProperty("title", "name");//将SysFunction对象里的属性名“title”映射到json对象里的属性名“name”
*
* //开始调用解析函数
* return
* TreeNode.parseListToJsonTree(treeList, "code","parentCode",p);
* } catch (Exception e) {
* e.printStackTrace();
* }
*
* @throws SecurityException java反射异常
* @throws NoSuchFieldException java反射异常
* @throws IllegalArgumentException java反射异常
* @throws IllegalAccessException java反射异常
*/
public static String parseListToJsonTree(List entities,
String key, String parentKey, Properties fieldPropertyMap) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
return parseListToJsonTree(entities,key,parentKey,fieldPropertyMap,null);
}
/**
* 将对象集合解析为json格式的树形脚本
* @param 集合成员类型
* @param entities 待解析的集合,必须具备有效值
* @param key 对象的唯一标识属性,必须具备有效值
* @param parentKey 父对象的唯一标识属性,必须具备有效值
* @return json格式的树形脚本
* 例如结果如下格式:
*
* [
* {id:11,name:'诺基亚'},
* {id:12,name:'三星',nodes:[
* {id:121,name:'I9000(联通版)'},
* {id:122,name:'I9000(移动版)'},
* {id:123,name:'Galaxy Naos'}
* ]},
* {id:13,name:'索爱'},
* {id:14,name:'TCL'},
* {id:15,name:'摩托罗拉'}
* ]
* 使用示范如下:
* * //用一个集合模拟数据表的内容,也就是一下集合的内容完全可以查询自数据库
* List treeList = new ArrayList();
* treeList.add(new SysFunction("L01", null, "营销管理","没有"));
* treeList.add(new SysFunction("L02",null,"客户管理","没有"));
* treeList.add(new SysFunction("L03",null,"服务管理","没有"));
* //营销管理的子功能
* treeList.add(new SysFunction("L0101","L01","创建销售机会","没有"));
* treeList.add(new SysFunction("L0102","L01","指派销售机会","没有"));
* treeList.add(new SysFunction("L0103","L01","制定开发计划","没有"));
* treeList.add(new SysFunction("L0104","L01","执行开发计划","没有"));
* //指派销售机会的子功能
* treeList.add(new SysFunction("L010201","L0102","经理指派","没有"));
* treeList.add(new SysFunction("L010202","L0102","主管指派","没有"));
* //客户管理的子功能
* treeList.add(new SysFunction("L0201","L02","客户信息管理","没有"));
* treeList.add(new SysFunction("L0202","L02","客户流失管理","没有"));
* //服务管理子功能
* treeList.add(new SysFunction("L0301","L03","咨询服务管理","没有"));
* treeList.add(new SysFunction("L0302","L03","投诉服务管理","没有"));
* treeList.add(new SysFunction("L0303","L03","查询服务管理","没有"));
*
* try {
* //开始调用解析函数
* return
* TreeNode.parseListToJsonTree(treeList, "code","parentCode");
* } catch (Exception e) {
* e.printStackTrace();
* }
*
* @throws SecurityException java反射异常
* @throws NoSuchFieldException java反射异常
* @throws IllegalArgumentException java反射异常
* @throws IllegalAccessException java反射异常
*/
public static String parseListToJsonTree(List entities,
String key, String parentKey) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
return parseListToJsonTree(entities,key,parentKey,null,null);
}
/**
* * 将当前TreeNode解析为JSON字串 构造的目标格式示例:
* {
* id:1,
* name:'手机',
* nodes:[
* {id:11,name:'诺基亚'},
* {id:12,name:'三星',nodes:[
* {id:121,name:'I9000(联通版)'},
* {id:122,name:'I9000(移动版)'},
* {id:123,name:'Galaxy Naos'}
* ]},
* {id:13,name:'索爱'}
* ]
* }
*
* */
private String parseTreeNode(TreeNode node) {
if (node == null)
throw new RuntimeException("节点不能为空");
StringBuilder nodeBuilder = new StringBuilder("{");// JSON对象开始
// 把Map中的键值对构造成json对象属性
for (Map.Entry kvp : node.getTag().entrySet()) {
if (kvp.getValue()==null || kvp.getValue().getClass() == String.class)
nodeBuilder.append(kvp.getKey() + ":\"" + kvp.getValue() + "\",");
else
nodeBuilder.append(kvp.getKey() + ":" + kvp.getValue() + ",");
}
// 构造子节点
nodeBuilder.append("nodes:["); // 子节点开始
for (TreeNode cnode : node.getNodes()) {
if (cnode == null)
continue;
nodeBuilder.append(parseTreeNode(cnode) + ",");// 递归调用ParseTreeNode返回一个完整的JSON字串:"{属性1:value,属性2:value}"
}
if (nodeBuilder.charAt(nodeBuilder.length() - 1) == ',')// 去掉末尾逗号
nodeBuilder.deleteCharAt(nodeBuilder.length() - 1);
nodeBuilder.append("]"); // 子节点结束
nodeBuilder.append("}"); // JSON对象结束
return nodeBuilder.toString();
}
/**
* 向指定节点及其子孙节点添加一个属性
*
* @author jiang
* @param node
* 被放置属性的节点
* @param isrecursive
* 是否递归[当前节点的子孙节点]放置属性
* @param entries
* 放置的属性键值对
*/
private void _putProperty(TreeNode node, Boolean isrecursive,
Map.Entry... entries) {
if (entries != null)
for (Map.Entry entry : entries) {
node.getTag().put(entry.getKey(), entry.getValue());
}
if (!isrecursive)
return;
for (TreeNode n : node.getNodes()) {
_putProperty(n, isrecursive, entries);
}
}
/**
* 向指定节点及其子孙节点添加一个属性
*
* @author jiang
* @param node
* 被放置属性的节点
* @param isrecursive
* 是否递归[当前节点的子孙节点]放置属性
* @param entries
* 放置的属性键值对
*/
public void putProperty(Boolean isrecursive,
Map.Entry... entries) {
_putProperty(this, isrecursive, entries);
}
public interface IParseTreeNodeCallBack{
void processTreeNode(TreeNode node);
}
}
我的测试代码如下:
package biz;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import model.TreeNode;
/**
* 系统功能对象
* */
class SysFunction {
private String code;
private String parentCode;
private String title;
private String desc;
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
/**功能编号*/
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**父级功能编号*/
public String getParentCode() {
return parentCode;
}
public void setParentCode(String parentCode) {
this.parentCode = parentCode;
}
/**功能名称,现在树上的节点文本*/
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public SysFunction(String code, String parentCode, String title) {
super();
this.code = code;
this.parentCode = parentCode;
this.title = title;
}
public SysFunction(String code, String parentCode, String title,String desc) {
super();
this.code = code;
this.parentCode = parentCode;
this.title = title;
this.desc = desc;
}
public SysFunction() {
super();
}
}
public class TreeNodeService {
public String getTree(){
//这里用一个集合模拟数据表的内容,也就是一下集合的内容完全可以查询自数据库
//存储树形的数据表应该满足以下基本机构:
/*CREATE TABLE JTREE
(
CODE INT PRIMARY KEY,//当前节点的ID,这个是必须字段,名字无所谓
PARENT_CODE INT REFERENCES JTREE(CODE),//当前节点的父节点ID,这个是必须字段,且必须符合父子引用关系,名字无所谓
TITLE VARCHAR2(32),//节点显示的文本
...,//节点其它信息
...//节点其它信息
);*/
List treeList = new ArrayList();
treeList.add(new SysFunction("L01", null, "营销管理","没有"));
treeList.add(new SysFunction("L02",null,"客户管理","没有"));
treeList.add(new SysFunction("L03",null,"服务管理","没有"));
//营销管理的子功能
treeList.add(new SysFunction("L0101","L01","创建销售机会","没有"));
treeList.add(new SysFunction("L0102","L01","指派销售机会","没有"));
treeList.add(new SysFunction("L0103","L01","制定开发计划","没有"));
treeList.add(new SysFunction("L0104","L01","执行开发计划","没有"));
//指派销售机会的子功能
treeList.add(new SysFunction("L010201","L0102","经理指派","没有"));
treeList.add(new SysFunction("L010202","L0102","主管指派","没有"));
//客户管理的子功能
treeList.add(new SysFunction("L0201","L02","客户信息管理","没有"));
treeList.add(new SysFunction("L0202","L02","客户流失管理","没有"));
//服务管理子功能
treeList.add(new SysFunction("L0301","L03","咨询服务管理","没有"));
treeList.add(new SysFunction("L0302","L03","投诉服务管理","没有"));
treeList.add(new SysFunction("L0303","L03","查询服务管理","没有"));
try {
Properties p = new Properties();
p.setProperty("code", "id");//将SysFunction对象里的属性名“code”映射到json对象里的属性名“id”
p.setProperty("title", "name");//将SysFunction对象里的属性名“title”映射到json对象里的属性名“name”
//开始调用解析函数,第五个参数是一个匿名内部类
return
TreeNode.parseListToJsonTree(treeList, "code","parentCode",p,new TreeNode.IParseTreeNodeCallBack() {
public void processTreeNode(TreeNode node) {
node.getTag().put("open",true);//向节点追加一个属性,表示要zTree展开这个节点
node.getTag().put("click","alert('"+node.getTag().get("id")+"')");//向节点追加一个属性click,表示要求zTree的节点具有响应点击事件的能力
}
});
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "[]";
}
}
注意id为“treeDemo3"的代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="biz.TreeNodeService"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
运行结果,如下图:
点击节点执行click事件弹出当前节点的编号,参考前面的代码如何实现:
TreeNode.parseListToJsonTree(treeList, "code","parentCode",p,new TreeNode.IParseTreeNodeCallBack() {
public void processTreeNode(TreeNode node) {
node.getTag().put("open",true);//向节点追加一个属性,表示要zTree展开这个节点
node.getTag().put("click","alert('"+node.getTag().get("id")+"')");//向节点追加一个属性click,表示要求zTree的节点具有响应点击事件的能力
}
});