1. 程式人生 > 實用技巧 >手寫XML轉化為JS物件方法

手寫XML轉化為JS物件方法

function xml(str, is = true){  //解析XML
    /* 定義返回的物件 */
    let result = {  //解析成功返回的物件(標籤名,屬性集合物件[屬性名:屬性值...],子元素陣列[{}, {}...])
      meta: '',
      xml: {}
    };
    /* 提取首行說明 */
    str = str.trim();  //字串去除兩邊空白
    for(let i = 0, len = str.length; i < len; ++i){  //提取首行宣告
      if(str[i] == '>'){
        result.meta 
= str.slice(0, i + 1); str = str.slice(i + 1); break; } } str = str.trim(); /* 找出可以分割的點 */ let subList = [], //切割點下標集合 state = false, //是否進入標籤區域 tempSub = -1; //臨時儲存下標 for(let i = 0, len = str.length; i < len; ++i){ if(state){ if(str[i] == '>'){ subList.push(i
+ 1); state = false; } }else{ if(str[i] == '<'){ if(tempSub != -1){ subList.push(tempSub); tempSub = -1; }else{} subList.push(i); state = true; }else{ if(tempSub == -1){ tempSub
= i; } } } } subList = Array.from(new Set(subList)); /* 執行分割 */ let elementList = []; for(let i = 0, len = subList.length - 1; i < len; ++i){ elementList.push(str.slice(subList[i], subList[i + 1])); } /* 校驗是否符合規則 */ let stackCheck = [], getEndLabelName = function(str){ str = str.replace(/\s*/g, ""); return str.slice(2, str.length - 1); }, getStartLabelName = function(str, is = true){ let start = -1; for(let i = 0, len = str.length; i < len; ++i){ if(start == -1){ if(str[i] != '<' && str[i] != ' '){ start = i; } }else{ if(str[i] == ' ' || str[i] == '>'){ if(is){ return str.slice(start, i); }else{ return i; } } } } }; for(let i = 0, len = elementList.length; i < len; ++i){ if(elementList[i][0] == '<'){ if(elementList[i].replace(/\s*/g, "")[1] == '/'){ //結束標籤 if(stackCheck[stackCheck.length - 1] != getEndLabelName(elementList[i])){ let tempTit = '標籤匹配錯誤:' + stackCheck[stackCheck.length - 1] + '與' + getEndLabelName(elementList[i]) + '不匹配'; console.error(tempTit); if(is){ return {}; }else{ return []; } }else{ stackCheck.splice(stackCheck.length - 1); } }else{ //開始標籤 stackCheck.push(getStartLabelName(elementList[i])); } } } /* 如果is為false */ if(!is){ elementList.unshift(result.meta) return elementList; } /* 構建零散物件,打上id和父級id */ stackCheck.push({ID: 0}); let eleObjList = [], //零散物件列表 ID = 0; //ID計數器 let getLabelAttribute = function(str){ str = str.slice(getStartLabelName(str, false)); str = str.slice(0, str.length - 1); str = str.trim(); if(str == ""){ return {}; } let stack = ""; for(let i = 0, len = str.length; i < len; ++i){ if(str[i] == "'" || str[i] == '"'){ if(stack == ""){ stack = str[i]; } else{ if(stack == str[i]){ stack = ""; }else{ return 0; } } } } let list = [], count = 0, start = -1; for(let i = 0, len = str.length; i < len; ++i){ if(start == -1){ if((str[i].charCodeAt(0) >= 65 && str[i].charCodeAt(0) <= 90) || (str[i].charCodeAt(0) >= 97 && str[i].charCodeAt(0) <= 122)){ start = i; } }else{ if(str[i] == "'" || str[i] == '"'){ ++count; } if(count == 2){ list.push(str.slice(start, i + 1)); start = -1; count = 0; } } } let result = {}; for(let i = 0, len = list.length; i < len; ++i){ let tempArr = list[i].split("="); tempArr[0] = tempArr[0].trim(); tempArr[1] = tempArr[1].trim(); tempArr[1] = tempArr[1].slice(1, tempArr[1].length - 1); result[tempArr[0]] = tempArr[1]; } return result; }; for(let i = 0, len = elementList.length; i < len; ++i){ if(elementList[i][0] == '<'){ if(elementList[i].replace(/\s*/g, "")[1] != '/'){ //開始標籤 ++ID; let temp = getLabelAttribute(elementList[i]); if(typeof temp == "number"){ console.error("屬性引號不匹配"); return; } eleObjList.push({ ID: ID, pID: stackCheck[stackCheck.length - 1].ID, label: getStartLabelName(elementList[i]), attribute: temp, children: [] }); stackCheck.push({ID: ID}); }else{ //結束標籤 stackCheck.splice(stackCheck.length - 1); } }else{ ++ID; eleObjList.push({ text: elementList[i], pID: stackCheck[stackCheck.length - 1].ID, ID: ID }); } } /* 對實體引用進行字元替換 */ let replaceXML = function(str){ str = str.replace(/&lt;/g, "<"); str = str.replace(/&gt;/g, ">"); str = str.replace(/&amp;/g, "&"); str = str.replace(/&apos;/g, "'"); str = str.replace(/&quot;/g, '"'); return str; }; for(let i = 0, len = eleObjList.length; i < len; ++i){ if(eleObjList[i].text == undefined){ for(let key in eleObjList[i].attribute){ eleObjList[i].attribute[key] = replaceXML(eleObjList[i].attribute[key]); } }else{ eleObjList[i].text = replaceXML(eleObjList[i].text); } } /* 構建最終物件 */ for(let i = eleObjList.length - 1; i > 0; --i){ eleObjList[eleObjList[i].pID - 1].children.unshift(eleObjList[i]); eleObjList[i].ID = eleObjList[i].pID = undefined; } eleObjList[0].ID = eleObjList[0].pID = undefined; result.xml = eleObjList[0]; return result; }

實現思路:

<1>提取出XML的首行宣告

<2>將XML拆分為 開始標籤 結束標籤 文字三部分

<3>利用棧結構校驗標籤是否正確匹配

<4>遍歷構建標籤物件,文字物件,併為他們打上ID和父級指向ID

<5>對實體引用進行字元替換

<5>把這些單個物件構建成最終的結果並返回

例子:

XML:

<?xml version="1.0" encoding="ISO-8859-1"?>

<biology type="people">

<name>lvyang</name>

<age>24</age>

<gender>male</gender>

<interest>

<item>吃飯</item>

<item>睡覺</item>

<item>玩</item>

</interest>

垃圾王者隊友不配贏

</biology>

構建的JS物件:

可自行驗證更復雜的XML