作者: 不要脸的死宅男
本人是一名正在培训机构培训前端的新人,因为最近课程不是很紧,于是闲着无聊便想着写这么一个东西。写这个的起因是因为自学XMLHttpRequest的时候,想要学jquery来把ajax进行简单的封装,然后进行返回数据处理编写的时候,因为对js如何将字符串转换成对象这里比较感兴趣,想要去查看一下源码,才发现不能看(现在才知道)。于是这正好激起了我的兴趣,便打算自己手动写一个方法来进行转换。
代码部分
/**
* 字符串转JSON对象
* @param {String} jsonStr 对象字符串
* @return {Object} 转换完的对象
*/
function stringToJSON(jsonStr) {
if (jsonStr[0] == "[" && jsonStr[jsonStr.length - 1] == "]")
return stringToJSONByArray(jsonStr);
else
return stringToJSONByOne(jsonStr);
/**
* 字符串转单个对象
* @param {String} jsonStr 对象字符串
* @return {Object} 转换后的对象
*/
function stringToJSONByOne(jsonStr) {
var obj = {};
//获取每一项键值对的字符串
var keyValArr = cusSplit(jsonStr, true);
for (var i = 0; i < keyValArr.length; i++) {
var curKeyVal = keyValArr[i];
//获取name和value的字符串数组
var nameAndVal = (function() {
var arrayTemp = [];
var tempVar = curKeyVal.trim();
var startIndex = 0;
arrayTemp.push(tempVar.substring(1, (startIndex = tempVar.indexOf(":")) - 1));
arrayTemp.push(tempVar.substring(startIndex + 1, tempVar.length));
return arrayTemp;
}());
var name = nameAndVal[0];
var property = nameAndVal[1];
//设置属性到对象上
setProertyToObjOrArray(property, function(result) {
obj[name] = result;
});
}
return obj;
}
/**
* 字符串转数组
* @param {String} jsonStr 数组字符串
* @returns {Object} 转换后的数组对象
*/
function stringToJSONByArray(jsonStr) {
var obj = [];
var jsonArray;
jsonArray = cusSplit(jsonStr, false);
for (var i = 0; i < jsonArray.length; i++) {
var curElement = jsonArray[i];
setProertyToObjOrArray(curElement, function(result) {
obj.push(result);
});
}
return obj;
}
/**
* 将json字符串的每一项存到数组中去
* 如果是对象字符串则将每个键值对字符串存到数组中去
* 如果是数组字符串则将每一项的字符串存到数组中去
* @param {String} strVar json字符串
* @param {Boolean} flag true是对象,false是数组
* @return {Array} 处理后的数组对象
*/
function cusSplit(strVar, flag) {
var resultArray = [];
var startIndex = 1;
var endIndex = 0;
while (true) {
if (flag) {
endIndex = strVar.indexOf("\":", endIndex) + 1;
//判断是不是空对象
if (endIndex == 0) {
resultArray.push({});
break;
}
}
//判断属性值的类型
var tempChar = strVar[endIndex += 1];
//判断是不是对象
if (tempChar == "{" || tempChar == "[") {
endIndex = getObjectEndIndex(strVar, endIndex, strVar[endIndex] == "{") + 1;
//判断是不是字符串
} else if (tempChar == "\"") {
var tempStrIndex = strVar.indexOf("\"", endIndex + 1);
while (true) {
//判断是不是字符串属性值的结束
if (strVar[tempStrIndex - 1] != "\\") {
endIndex = tempStrIndex + 1;
break;
}
tempStrIndex = strVar.indexOf("\"", tempStrIndex + 1);
}
} else {
var tempIndex = strVar.indexOf(",", endIndex);
//判读是否到对象的结束
if (tempIndex == -1) {
endIndex = strVar.indexOf(flag ? "}" : "]", endIndex);
resultArray.push(strVar.slice(startIndex, endIndex));
break;
} else
endIndex = tempIndex;
}
resultArray.push(strVar.slice(startIndex, endIndex));
//判断是否结束
if (strVar[endIndex] == (flag ? "}" : "]"))
break;
startIndex = endIndex + 1;
}
return resultArray;
}
/**
* 根据开始的位置开始找寻第一个json对象结束的索引
* @param {String} jsonStrVar json字符串
* @param {Number} startIndex 开始索引的位置
* @param {Boolean} flag 是对象还是数组,true对象false数组
* @return {Number} 对象字符串的结束索引
*/
function getObjectEndIndex(jsonStrVar, startIndex, flag) {
//对象结束的索引
var resultIndex;
//进行偏移的临时索引,最后需要将值赋给resultIndex变量
var tempStrIndex;
/**
* @param {Number} startIndex 左括号(中括号)的索引
* @param {Boolean} flag true对象false数组
*/
function objectIndexProcess(startIndex, flag) {
tempStrIndex = jsonStrVar.indexOf(flag ? "{" : "[", startIndex);
resultIndex = tempStrIndex;
if (tempStrIndex == -1)
return;
while (true) {
if (flag) {
tempStrIndex = jsonStrVar.indexOf("\":", resultIndex) + 1;
//判断是不是空对象
if (tempStrIndex == 0) {
resultIndex += 1;
return;
}
}
//判断当前属性值的类型
var tempVar = jsonStrVar[tempStrIndex += 1];
//判断是不是对象
if (tempVar == "{" || tempVar == "[")
objectIndexProcess(tempStrIndex, tempVar == "{");
//判断是不是字符串
else if (tempVar == "\"") {
tempEndIndex = jsonStrVar.indexOf("\"", tempStrIndex + 1);
while (true) {
//判断是不是字符串的结尾
if (jsonStrVar[tempEndIndex - 1] != "\\") {
tempStrIndex = tempEndIndex;
break;
}
tempEndIndex = jsonStrVar.indexOf("\"", tempEndIndex + 1);
}
} else {
var first = jsonStrVar.indexOf(flag ? "}" : "]", tempStrIndex);
var second = jsonStrVar.indexOf(",", tempStrIndex);
//判断是不是最后一个属性值
if (second < first && second != -1)
tempStrIndex = jsonStrVar.indexOf(",", tempStrIndex);
else {
tempStrIndex = first;
resultIndex = tempStrIndex;
break;
}
}
//判断是不是到结尾了
if (jsonStrVar[tempStrIndex + 1] == (flag ? "}" : "]")) {
resultIndex = (tempStrIndex += 1);
break;
}
resultIndex = tempStrIndex;
}
}
objectIndexProcess(startIndex, flag);
return resultIndex;
}
/**
* 对属性值进行解析然后赋值给对应的对象或者数组
* @param {String} curElement 属性值字符串
* @param {Function} process 处理结果的回调函数
*/
function setProertyToObjOrArray(curElement, process) {
//判读是不是对象
if (curElement[0] == "{" && curElement[curElement.length - 1] == "}") {
process(stringToJSONByOne(curElement));
//判读是不是数组
} else if (curElement[0] == "[" && curElement[curElement.length - 1] == "]") {
process(stringToJSONByArray(curElement));
//判断是不是布尔类型
} else if (curElement == "true" || curElement == "false") {
process(curElement == "true");
//判断是不是字符串
} else if (curElement[0] == "\"" && curElement[curElement.length - 1] == "\"") {
process(curElement.slice(1, curElement.length - 1));
} else {
process(Number(curElement));
}
}
}
整段代码的逻辑其实很简单,就是通过对象(数组)字符串进行分割将每一项(键值对或者数组元素)的字符串分割出来,然后进行处理。这段代码最核心的地方就是如何确定对象(数组)字符串每一项的开始和结束。我主要通过getObjectEndIndex()这个自己编写的方法来进行处理的,它的逻辑就是找寻每一项的时候先判断当前键值对(元素)是否是一个对象或者数组,如果是就进行递归将该方法中的索引变量的值进行改变,不是就进行字符串和其他值的判断。最终改变索引变量的值来获取当前键值对(元素)的结束索引进行返回。方法的注释也都在代码上了,大家可以参考一下。
因为我只是个菜鸟也不知道这个代码还有什么问题,所以希望各位大佬能够多多指教,帮忙提出改进建议或者存在的bug。