手写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(/&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

以上是关于手写XML转化为JS对象方法的主要内容,如果未能解决你的问题,请参考以下文章

JS解析xml字符串,并把xml展示在HTML页面上

面试系列番外:关于糯米面试

通过JAXB完成Java对象与XML之间的转换

如何把js字符串转化为对象?

音频视频播放(jquery中将jquery方法转化成js方法)

手写js代码格式化json数据