手写XML转化为JS对象方法
Posted zhenfeishi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写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(/</g, "<"); str = str.replace(/>/g, ">"); str = str.replace(/&/g, "&"); str = str.replace(/'/g, "‘"); str = str.replace(/"/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