如何通過遞歸方法實(shí)現(xiàn)用json-diff渲染json字符串對(duì)比結(jié)果
前言
上一篇,對(duì)比了js-diff和json-diff,發(fā)現(xiàn)js-diff對(duì)比的結(jié)果返回的是拆分后字符串并且字符串中帶有換行符跟空格,將拆分后的字符串拼接可以得到完整的兩個(gè)json格式化后的信息。但是json-diff只返回了兩個(gè)json中有修改的的部分,并且返回類型還要根據(jù)值類型來進(jìn)行判斷。
分析json-diff的結(jié)構(gòu)
用兩個(gè)數(shù)組嵌套對(duì)象,對(duì)象嵌套數(shù)組的比較復(fù)雜的json進(jìn)行對(duì)比,獲得的數(shù)據(jù)如下:
var json1 = { name: '小明', age: '22', hobby: ['籃球', '足球', '羽毛球'], grade: { English: ['130', '129', '135'], Chinese: ['120', '118', '122'], sports: ['90', '90', '90'], }, honor: [{ desc: '獲得數(shù)學(xué)競(jìng)賽第一名', info: [{ score: '100', ranking: 1 }, 2, 3] }], } var json2 = { name: '小紅', age: '22', hobby: ['乒乓球', '羽毛球'], grade: { English: ['120', '129', '125'], Chinese: ['140', '139', '136'], sports: ['90', '90', '90'], }, honor: [{ desc: '獲得作文比賽第一名', info: [{ score: '99', rank: 2 }, 2, 3] }], }
var jsonDiff = { "name": { "__old": "小明", "__new": "小紅" }, "hobby": [ [ "-", "籃球" ], [ "-", "足球" ], [ "+", "乒乓球" ], [ " " ] ], "grade": { "English": [ [ "-", "130" ], [ "+", "120" ], [ " " ], [ "-", "135" ], [ "+", "125" ] ], "Chinese": [ [ "-", "120" ], [ "-", "118" ], [ "-", "122" ], [ "+", "140" ], [ "+", "139" ], [ "+", "136" ] ] }, "honor": [ [ "~", { "desc": { "__old": "獲得數(shù)學(xué)競(jìng)賽第一名", "__new": "獲得作文比賽第一名" }, "info": [ [ "~", { "ranking__deleted": 1, "rank__added": 2, "score": { "__old": "100", "__new": "99" } } ], [ " " ], [ " " ] ] } ] ] };
可以觀察到:
- 字符串返回{"__old":string,"__new":string},數(shù)組返回[string[,string][,object]],非數(shù)組對(duì)象返回{ "key__added": string|object, "key__deleted": string|object, "key": string|object }。
- 字符串通過__old、__new獲取
- 非數(shù)組對(duì)象新增屬性從key__added獲取,刪除屬性從key__deleted獲取
- 數(shù)組第一個(gè)值有四種類型空格表示未修改、+表示新增、-表示刪除、~表示修改
解析結(jié)構(gòu)圖如下:
太繞了,畫圖之后才看的明白一點(diǎn)ORZ...
用遞歸方法拼接json字符串
首先創(chuàng)建一個(gè)函數(shù)用來判斷json是否為數(shù)組,renderobj(diffObj, originObj, n),diffObj表示當(dāng)層的diff對(duì)象,originObj表示當(dāng)層的json1對(duì)象,n表示深度,第一層深度為0,主要為了方便計(jì)算縮進(jìn)。
renderobj函數(shù):
// 定義remove和add隊(duì)列 const removeList = []; const addlist = []; if (diffObj instanceof Array) { // 數(shù)組的判斷 } else if (typeof diffObj == 'object'){ if (diffObj.__new) { // 字符串修改 } else { // 非數(shù)組對(duì)象修改 } }
字符串修改
當(dāng)diffObj.__new為真時(shí),__old同時(shí)有值,表示字符串做修改,此時(shí)可以直接把__new放到addList,__old存入removeList。
addlist.push(`<span style="color:red;">"${diffObj.__new}"</span>,<br/>`); removeList.push(`<span style="color:red;">"${diffObj.__old}"</span>,<br/>`);
非數(shù)組對(duì)象修改
const { addHtml, removeHtml } = renderObject(diffObj, originObj, n); addlist.push(addHtml); removeList.push(removeHtml);
非數(shù)組對(duì)象要判斷key的值,創(chuàng)建函數(shù)renderObject(diffObj, originObj, n),傳參同renderobj。
const addlist = []; const removeList = []; addlist.push(`{</br>`); removeList.push(`{</br>`); // 遍歷originObj,遍歷diffObj... addlist.push(`${spaceStr.repeat(n)}},</br>`); removeList.push(`${spaceStr.repeat(n)}},</br>`); return { addHtml: addlist.join(''), removeHtml: removeList.join(''), };
為啥要分別遍歷originObj和diffObj?因?yàn)閖son1對(duì)象中沒有新增的key,diffObj中沒有返回未修改key。
const spaceStr = ' ';
Object.keys(originObj).forEach(key => { const keyVal = diffObj[key]; if (keyVal) { // renderobj重新判斷修改類型 const { addHtml, removeHtml } = renderobj(keyVal, originObj[key], n + 1); if (keyVal.__new) { // 當(dāng)字符串修改時(shí),將整行標(biāo)紅 addlist.push('<div class="error-line-add">'); removeList.push('<div class="error-line-remove">'); } addlist.push(`${spaceStr.repeat(n + 1)}"${key}": ${addHtml}`); removeList.push(`${spaceStr.repeat(n + 1)}"${key}": ${removeHtml}`); if (keyVal.__new) { addlist.push(`</div>`); removeList.push(`</div>`); } } else { const remove = diffObj[key + '__deleted']; if (remove) { // 刪除的屬性 removeList.push('<div class="error-line-remove">'); removeList.push( `<span style="color:red;">${spaceStr.repeat(n + 1)}"${key}": ${renderJson( remove, n + 1 )}</span></br>` ); removeList.push('</div>'); } else { // 沒修改的屬性直接存入 addlist.push( `${spaceStr.repeat(n + 1)}"${key}": ${renderJson(originObj[key], n + 1)}</br>` ); removeList.push( `${spaceStr.repeat(n + 1)}"${key}": ${renderJson(originObj[key], n + 1)}</br>` ); } } })
Object.keys(diffObj).forEach(key => { // 新增的屬性,json1中沒有,從diffObj中遍歷 if (key.includes('__added')) { addlist.push('<div class="error-line-add">'); addlist.push( `<span style="color: red;">${spaceStr.repeat(n + 1)}"${key.replace('__added', '')}": "${diffObj[key]}",</br></span>` ); addlist.push('</div>'); } });
數(shù)組對(duì)象修改
遍歷diffObj,判斷item[0]的值,當(dāng)值為空格時(shí),需要從json1中獲取原屬性值,diffObj中空格所在的索引位置有可能和originObj中索引的位置不同,要排除掉'+'新增的成員;當(dāng)值為'+'時(shí),直接將值存入addList;當(dāng)值為'-'時(shí),直接將值存入removeList;當(dāng)值為'~'時(shí),表示有修改,修改類型不確定需要重新循環(huán)判斷。
addlist.push(`[</br>`); removeList.push(`[</br>`); diffObj.forEach((item, i) => { switch (item[0]) { case '~': { // 有修改,重新判斷修改的值的類型 const { addHtml, removeHtml } = renderobj(item[1], originObj[i], n + 1); addlist.push(`${spaceStr.repeat(n + 1)}${addHtml}`); removeList.push(`${spaceStr.repeat(n + 1)}${removeHtml}`); break; } case '-': { // 刪除屬性 removeList.push('<div class="error-line-remove">'); removeList.push( `<span style="color: red;">${spaceStr.repeat(n + 1)}${renderJson( item[1], n + 1 )}</br>` ); removeList.push('</div>'); break; } case '+': { // 新增屬性 addlist.push('<div class="error-line-add">'); addlist.push( `<span style="color: red;">${spaceStr.repeat(n + 1)}${renderJson( item[1], n + 1 )}</br>` ); addlist.push('</div>'); break; } case ' ': { // 屬性未修改,從originObj中獲取,注意對(duì)應(yīng)的index不包括新增的屬性 const index = diffObj.slice(0, i).filter(item => item[0] != '+').length; const value = originObj[index]; const itemStr = `${spaceStr.repeat(n + 1)}${renderJson(value, n + 1)}</br>`; addlist.push(itemStr); removeList.push(itemStr); break; } } }); addlist.push(`${spaceStr.repeat(n)}],</br>`); removeList.push(`${spaceStr.repeat(n)}],</br>`);
renderJson函數(shù),用來格式化json值:
const renderJson = (json, n) => { if (!json) { return ""; } if (typeof json == "string" || typeof json == "number") { return `"${json}",`; } return `${JSON.stringify(json, null, "\t") .replace(new RegExp("\n", "g"), `</br>${spaceStr.repeat(n)}`) .replace(new RegExp("\t", "g"), spaceStr.repeat(n))},`; };
最后對(duì)比顯示結(jié)果如下:
總結(jié)
到此這篇關(guān)于如何通過遞歸方法實(shí)現(xiàn)用json-diff渲染json字符串對(duì)比結(jié)果的文章就介紹到這了,更多相關(guān)json-diff渲染json字符串對(duì)比結(jié)果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Electron應(yīng)用顯示隱藏時(shí)展示動(dòng)畫效果實(shí)例
最近使用electron實(shí)現(xiàn)一個(gè)簡(jiǎn)單的功能,下面這篇文章主要給大家介紹了關(guān)于Electron應(yīng)用顯示隱藏時(shí)展示動(dòng)畫效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05Bootstrap DateTime Picker日歷控件簡(jiǎn)單應(yīng)用
這篇文章主要介紹了Bootstrap DateTime Picker日歷控件的簡(jiǎn)單應(yīng)用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03用JS實(shí)現(xiàn)3D球狀標(biāo)簽云示例代碼
3D球狀標(biāo)簽云的效果想必很多朋友在瀏覽網(wǎng)頁時(shí)都有見到過吧,看起來提復(fù)雜的,其實(shí)實(shí)現(xiàn)起來挺容易的,感興趣的朋友可以了解下本文2013-12-12微信小程序ajax實(shí)現(xiàn)請(qǐng)求服務(wù)器數(shù)據(jù)及模版遍歷數(shù)據(jù)功能示例
這篇文章主要介紹了微信小程序ajax實(shí)現(xiàn)請(qǐng)求服務(wù)器數(shù)據(jù)及模版遍歷數(shù)據(jù)功能,結(jié)合實(shí)例形式分析了微信小程序ajax調(diào)用及模板wx:for循環(huán)列表渲染相關(guān)操作技巧,需要的朋友可以參考下2017-12-12JavaScript中textRange對(duì)象使用方法小結(jié)
這篇文章主要介紹了JavaScript中textRange對(duì)象使用方法小結(jié),需要的朋友可以參考下2015-03-03layui中的switch開關(guān)實(shí)現(xiàn)方法
今天小編就為大家分享一篇layui中的switch開關(guān)實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09JavaScript 實(shí)現(xiàn)鼠標(biāo)拖動(dòng)元素實(shí)例代碼
這篇文章主要介紹了JavaScript 實(shí)現(xiàn)鼠標(biāo)拖動(dòng)元素實(shí)例代碼,需要的朋友可以參考下2014-02-02