需求:
前端页面需要展示三级菜单,后端返回结果集。问题在于,JavaBean中的属性值比实际需要的数量多,为了节省后端传递给前端的流量,将不需要的属性值设为null,这样转换为Json字符串的长度会小很多。
难点在于如何将id和parentid设置为null
数据结构要求如下:

1 [ 2 { 3 name:"一级菜单" 4 menus:[ 5 { 6 name:"二级菜单" 7 menus:[ 8 { 9 name:"三级菜单" 10 }, 11 ....... 12 ] 13 }, 14 ....... 15 ] 16 }, 17 ....... 18 ]
数据库中数据表:

1 CREATE TABLE `tb_ad` ( 2 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', 3 `name` varchar(50) DEFAULT NULL COMMENT '广告名称', 4 `position` varchar(50) DEFAULT NULL COMMENT '广告位置', 5 `start_time` datetime DEFAULT NULL COMMENT '开始时间', 6 `end_time` datetime DEFAULT NULL COMMENT '到期时间', 7 `status` char(1) DEFAULT NULL COMMENT '状态', 8 `image` varchar(100) DEFAULT NULL COMMENT '图片地址', 9 `url` varchar(100) DEFAULT NULL COMMENT 'URL', 10 `remarks` varchar(1000) DEFAULT NULL COMMENT '备注', 11 PRIMARY KEY (`id`) 12 )
对应的JavaBean如下:

1 @Table(name="tb_category") 2 public class Category implements Serializable{ 3 4 @Id 5 private Integer id;//分类ID 6 7 private String name;//分类名称 8 9 private Integer goodsNum;//商品数量 10 11 private String isShow;//是否显示 12 13 private String isMenu;//是否导航 14 15 private Integer seq;//排序 16 17 private Integer parentId;//上级ID 18 19 private Integer templateId;//模板ID 20 private List<Category> menus; 21 //省略get/set 22 23 }
方式一,在双层嵌套循环的过程中,在适当位置将id和parentid赋值为null。
优点,代码编写简单,测试容易。缺点,对于非包装类型无能为力,逻辑有点绕。

1 public List<Category> findCategoryTree() { 2 //1.查找所有需要显示的category 3 Example example = new Example(Category.class); 4 Example.Criteria criteria = example.createCriteria(); 5 criteria.andEqualTo("isShow","1"); 6 example.setOrderByClause("seq");//排序 7 List<Category> categories = categoryMapper.selectByExample(example); 8 ArrayList<Category> parentList = new ArrayList<>(); 9 //2.通过递归的方式,给menus赋值 10 for (Category category : categories) { 11 //一级category 12 if(Objects.nonNull(category.getParentId())&&category.getParentId()==0){ 13 //清除一级目录的parentId 14 category.setParentId(null); 15 parentList.add(category); 16 } 17 findChildren(categories,category); 18 category.setGoodsNum(null); 19 category.setIsMenu(null); 20 category.setIsShow(null); 21 category.setSeq(null); 22 category.setTemplateId(null); 23 } 24 return parentList; 25 } 26 27 private void findChildren(List<Category> categories, Category category){ 28 List<Category> list=new ArrayList<>(); 29 for (Category category1 : categories) { 30 if(category.getId().equals(category1.getParentId())){ 31 //到此说明已经找到它的father,那么parentId已经没有用了 32 category1.setParentId(null); 33 list.add(category1); 34 } 35 } 36 //到此说明已经找到它所有的children,那么id已经没有用了 37 category.setId(null); 38 if(list.size()>0){ 39 category.setMenus(list); 40 } 41 }
方式二,讲树状的结果集转为json字符串,利用正则表达式替换掉不需要的属性,然后再将字符串转为Object。
优点,对于非包装类的属性也适用,代码重复利用性高。缺点,会生成多个字符串常量,占用内存稍高,正则表达式写起来麻烦,不容易测试。

1 public Object findCategoryTree() { 2 //1.查找所有需要显示的category 3 Example example = new Example(Category.class); 4 Example.Criteria criteria = example.createCriteria(); 5 criteria.andEqualTo("isShow","1"); 6 example.setOrderByClause("seq");//排序 7 List<Category> categories = categoryMapper.selectByExample(example); 8 ArrayList<Category> parentList = new ArrayList<>(); 9 //2.通过递归的方式,给menus赋值 10 for (Category category : categories) { 11 //一级category 12 if(Objects.nonNull(category.getParentId())&&category.getParentId()==0){ 13 //清除一级目录的parentId 14 //category.setParentId(null); 15 parentList.add(category); 16 } 17 findChildren(categories,category); 18 category.setGoodsNum(null); 19 category.setIsMenu(null); 20 category.setIsShow(null); 21 category.setSeq(null); 22 category.setTemplateId(null); 23 } 24 //3.通过自定义方法将"id","parentId"属性移除 25 ArrayList<String> attributes = new ArrayList<>(); 26 attributes.add("id"); 27 attributes.add("parentId"); 28 return AttributeUtils.removeAttribute(parentList,attributes); 29 } 30 31 private void findChildren(List<Category> categories, Category category){ 32 List<Category> list=new ArrayList<>(); 33 for (Category category1 : categories) { 34 if(category.getId().equals(category1.getParentId())){ 35 //到此说明已经找到它的father,那么parentId已经没有用了 36 //category1.setParentId(null); 37 list.add(category1); 38 } 39 } 40 //到此说明已经找到它所有的children,那么id已经没有用了 41 //category.setId(null); 42 if(list.size()>0){ 43 category.setMenus(list); 44 } 45 }
自定义工具类

1 public class AttributeUtils { 2 public static <T> Object removeAttribute(List<T> list, List<String> attributeNames) { 3 //1.将list转为json字符串 4 String jsonString = JSONObject.toJSONString(list); 5 //2.删除掉json字符串中相关属性及属性值 6 for (String attributeName : attributeNames) { 7 //元素在开头 8 String regex1 = "[^,]\""+attributeName+"\":*[^}]*?,"; 9 Pattern p1 = Pattern.compile(regex1); 10 Matcher m1 = p1.matcher(jsonString); 11 jsonString = m1.replaceAll("{"); 12 //元素在尾部 13 String regex2 = ",\""+attributeName+"\":*[^{]*?}"; 14 Pattern p2 = Pattern.compile(regex2); 15 Matcher m2 = p2.matcher(jsonString); 16 jsonString=m2.replaceAll("}"); 17 //元素在中间 18 String regex3 = ",\""+attributeName+"\":*[^{]*?,"; 19 Pattern p3 = Pattern.compile(regex3); 20 Matcher m3 = p3.matcher(jsonString); 21 jsonString=m3.replaceAll(""); 22 } 23 Object parse = JSONObject.parse(jsonString); 24 return parse; 25 } 26 }