SpringBoot系列(九):解析前端請求“無限嵌套層級(jí)的列表數(shù)據(jù)”


作者: 修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

摘要:本文我們將分享介紹后端如何解析快速、高效地解析前端某些奇葩請求中的某些奇葩數(shù)據(jù),“無限嵌套的層級(jí)列表數(shù)據(jù)”便是其中的一種,在本文我們將介紹如何奇妙地利用“遞歸”算法層級(jí)遍歷并獲取相應(yīng)的層級(jí)列表數(shù)據(jù),并將其封裝成對(duì)象,最終將其更新至數(shù)據(jù)庫表中。

內(nèi)容:正常情況下,后端接口對(duì)于前端請求傳遞過來的數(shù)據(jù)一般都是了然于胸的,從而在后端接口解析期間,也就知道了前端傳遞的數(shù)據(jù)對(duì)應(yīng)的字段的含義,最終也就能“胸有成竹”般的正常解析完。

但有些時(shí)候,也存在著一些奇葩情況,如下圖所示的“菜單請求傳遞過來的層級(jí)列表數(shù)據(jù)”:


如果,前端傳遞過來的只有 id、parentId、name三個(gè)字段,那么直接采用某個(gè)類實(shí)例直接接受即可(這應(yīng)該沒啥難度)!

但是現(xiàn)在多了一個(gè)sons,sons里面也是一堆實(shí)體,每個(gè)實(shí)體也包含id、parentId、name三個(gè)字段 加一個(gè) sons 列表字段,以此類推下去,即所謂的“無限層級(jí)嵌套列表數(shù)據(jù)”!

此時(shí),如果你采用傳統(tǒng) for 或者 while循環(huán)遍歷sons進(jìn)行處理,那是行不通的,不行的話,各位小伙伴可以試試!

為啥不行呢:因?yàn)槟銐焊恢浪短琢硕嗌賹蛹?jí),而且每個(gè)層級(jí)里面本身還要再去處理id、parentId、name這樣的“父實(shí)體”信息,該實(shí)體信息下可能還有sons嵌套層級(jí)列表數(shù)據(jù)(簡直“瘋掉”?。?/span>

明人不說暗話,其實(shí),這種數(shù)據(jù)案例就是一種典型的“漢諾塔”遞歸實(shí)例。下面,我們采用“遞歸”的思想來實(shí)現(xiàn)這種功能續(xù)期吧

首先是建立一個(gè)請求方法用于接收前端請求中的數(shù)據(jù),該請求方法位于BaseController中,如下所示:

@Autowired
private MenuService menuService;

@RequestMapping(value = "/menu",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse menu(@RequestBody MenuDto menuDto){
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
menuService.manageMenu(menuDto);
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}

其中,MenuDto類代碼如下所示:  

@Data
public class MenuDto implements Serializable{
private Integer id;
private Integer parentId;
private String name;

private List<MenuDto> sons;
}

其中,MenuService的manageMenu方法的核心實(shí)現(xiàn)邏輯如下所示:  

@Service
public class MenuService {

private static final Logger log= LoggerFactory.getLogger(MenuService.class);

public void manageMenu(MenuDto menuDto) throws Exception{
log.info("接收到前端菜單層級(jí)列表樹:{}",menuDto);

List<MenuEntity> resList= Lists.newLinkedList();
circleMenu(menuDto,resList);

log.info("處理結(jié)果:{}",resList);
for (MenuEntity entity:resList){
log.info("遍歷可以準(zhǔn)備插入數(shù)據(jù)庫:{} ",entity);
}
}
}


其中的circleMenu()方法即為“遞歸算法”的核心所在:  

private void circleMenu(MenuDto dto,List<MenuEntity> resList){
MenuEntity entity=new MenuEntity();
entity.setId(dto.getId());
entity.setParentId(dto.getParentId());
entity.setName(dto.getName());

resList.add(entity);

List<MenuDto> sons=dto.getSons();
if (sons!=null && !sons.isEmpty()){
sons.stream().forEach(m -> circleMenu(m,resList));
}
}

其核心思想其實(shí)在于“遞歸”無限級(jí)的調(diào)用自己的“方法”,如下圖所示:  


最后,我們進(jìn)入測試環(huán)節(jié),廢話不多講,直接Postman的請求示意圖:  


再觀察Console控制臺(tái)的輸出信息,可以看到處理結(jié)果是OK的,如下圖所示:  


當(dāng)然啦,世間萬物,向來是“有一陰,必有一陽”,有優(yōu)點(diǎn),也勢必有其缺點(diǎn)所在,“遞歸算法”也是如此,它也肯定無法處理“層級(jí)數(shù)”無窮大的情況(即棧的深度是有講究的,這一原理各位小伙伴百度即可?。?nbsp; 

補(bǔ)充:

1、本文涉及到的相關(guān)的源代碼可以到此地址,check出來進(jìn)行查看學(xué)習(xí):

https://gitee.com/steadyjack/SpringBootTechnology

2、目前Debug已將本文所涉及的內(nèi)容整理錄制成視頻教程,感興趣的小伙伴可以前往觀看學(xué)習(xí):

https://www.fightjava.com/web/index/course/detail/5

3、關(guān)注一下Debug的技術(shù)微信公眾號(hào),最新的技術(shù)文章、技術(shù)課程以及技術(shù)專欄將會(huì)第一時(shí)間在公眾號(hào)發(fā)布哦!