写在前面
平时工作中会大量用到 JSON.stringify、JSON.parse 处理 JSON 数据。因为知道 JSON.parse 在解析的时候如果遇到格式不符的字符串会报错,所以一般会用 try...catch... 函数包裹 JSON.parse,避免抛出错误终止程序执行。JSON.stringify 一般没有做处理,今天在开发过程中遇到 JSON.stringify 处理循环引用的对象,导致程序终止。所以特意总结一下 JSON.stringfy、JSON.parse 使用及注意问题,希望以后在开发工程中避免一些错误问题。
简要介绍
JSON.stringify() 和 JSON.parse() 是 JavaScript 中用于处理 JSON 数据的两个重要方法。
1. JSON.stringify():
-
介绍:
- 当处理对象时,该方法会将对象转换为 JSON 字符串。
- 可以传入第二个参数用于指定替换或过滤属性的回调函数,或者传入一个数组用于指定要序列化的属性列表。
- 可以传入第三个参数用于指定缩进的空格数,以便获得格式化的输出。
-
注意事项:
- 注意循环引用的问题:如果对象存在循环引用,即对象中的某个属性引用了自身或引用了对象链中的其他对象,JSON.stringify() 会抛出异常。
2. JSON.parse():
-
介绍:
- 该方法用于将 JSON 字符串解析为 JavaScript 对象。
- 可以传入第二个参数用于自定义解析器,允许更灵活地处理数据。
-
注意事项:
- 如果传入的字符串无效或不是有效的 JSON 格式,JSON.parse() 会抛出异常。
- 如果 JSON 字符串中包含函数、正则表达式或日期等特殊类型,解析后的结果将失去相应的特殊类型,而被转换为字符串。
JSON.stringify 使用及注意问题
只传一个参数
javascript
const obj = {
name: "lin",
age: 18,
city: "Beijing"
};
const jsonString1 = JSON.stringify(obj);
console.log(jsonString1); // '{"name":"John","age":30,"city":"New York"}'
传入第二个参数
(1)传入一个数组来指定要序列化的属性列表
使用第二个参数传入一个数组来指定要序列化的属性列表:
javascript
obj = {
name: 'lin',
age: 18,
city: 'Beijing'
};
// 指定要序列化的属性列表
let jsonString = JSON.stringify(obj, ['name', 'age']);
console.log(jsonString); // 输出结果: {"name":"lin","age":18}
传入一个数组 ['name', 'age'] 作为第二个参数,指定了要序列化的属性列表。最终得到的 jsonString 只包含了指定的属性 "name" 和 "age",而 "city" 属性被排除在外。
(2)传入一个函数实现更复杂的属性控制
使用 JSON.stringify 的第二个参数 keyFilter 来指定替换或过滤属性的回调函数。这个回调函数可以帮助你控制 JSON 序列化的过程,从而避免循环引用问题。举个简单的例子:
javascript
circularObj = {};
circularObj.circularKey = circularObj;
const keyFilter = (key, value) => {
if (key === 'circularKey') {
return 'Circular Reference Detected!';
}
return value;
};
const jsonString = JSON.stringify(circularObj, keyFilter);
console.log(jsonString);
在这个例子中,我们定义了一个包含循环引用的对象 circularObj,并使用 keyFilter 函数来检测并替换循环引用属性。这样就可以避免循环引用问题导致的 JSON 序列化失败。
(3)传入第三个参数指定缩进的空格数
javascript
const obj = {
name: "lin",
age: 18,
city: "Beijing"
};
const jsonString1 = JSON.stringify(obj);
console.log(jsonString1); // '{"name":"John","age":30,"city":"New York"}'
const jsonString2 = JSON.stringify(obj, null, 2);
console.log(jsonString2); // '{\n "name": "John",\n "age": 30,\n "city": "New York"\n}'
注意循环引用问题
循环引用示例
下面是一个简单的循环引用的例子:
javascript
// 创建一个循环引用的对象
let obj = {};
obj.prop = obj;
// 尝试序列化这个对象
let jsonString = JSON.stringify(obj);
// 尝试解析这个字符串
let parsedObj = JSON.parse(jsonString);
console.log(parsedObj); // 在此处会抛出异常,因为 JSON 格式不支持循环引用
JSON 格式不支持循环引用。因此,在使用 JSON.stringify() 和 JSON.parse() 时,需要确保要序列化的对象中不存在循环引用的情况。
如何解决循环引用问题
如果无法某一个对象就是需要循环引用,在处理的时候可以使用 JSON.stringify 的第二个参数来指定替换或过滤属性的回调函数。在回调函数里可以控制 JSON 序列化的过程,从而避免循环引用问题。举个例子:
javascript
circularObj = {};
circularObj.circularKey = circularObj;
const keyFilter = (key, value) => {
if (key === 'circularKey') {
return 'Circular Reference Detected!';
}
return value;
};
const jsonString = JSON.stringify(circularObj, keyFilter);
console.log(jsonString); // {"circularKey":"Circular key Detected!"}
在这个例子中,定义了一个包含循环引用的对象 circularObj,并使用 keyFilter 函数来检测并替换循环引用属性。这样就可以避免循环引用问题导致的 JSON 序列化失败。
JSON.parse 使用及注意问题
传入第二个参数用于自定义解析器
这样可以更灵活地处理数据
javascript
const jsonStr = '{"name": "lin", "age": 18, "isStudent": false, "date": "2024-03-05"}';
const changeDate = (key, value) => {
if (key === 'date') {
// 将日期字符串转换为 Date 对象
return new Date(value);
}
return value;
};
const parsedData = JSON.parse(jsonStr, changeDate);
console.log(parsedData);
注意问题
传入的字符串无效或不是有效的 JSON 格式
这种情况下, JSON.parse() 方法会抛出异常。
javascript
const invalidJsonStr = '{name: "lin", age: 18}';
try {
const parsedData = JSON.parse(invalidJsonStr);
console.log(parsedData);
} catch (error) {
// Error parsing JSON: Expected property name or '}' in JSON at position 1 (line 1 column 2)
console.error('Error parsing JSON: ' + error.message);
}
在这个例子中,我们定义的字符串没有用双引号包裹属性名。当我们尝试使用 JSON.parse() 解析这个无效的 JSON 字符串时,会抛出异常。通过 try...catch 块,我们可以捕获并处理这个异常,避免程序终止。
字符串中包含函数、正则表达式或日期等特殊类型
这种情况下,解析后的结果将失去相应的特殊类型,而被转换为字符串。因为 JSON 格式本身只能表示基本数据类型,无法直接表示这些特殊类型。示例:
javascript
const data = {
name: 'lin',
func: function() {
console.log('Hello!');
},
regex: /hello/,
date: new Date()
};
const jsonString = JSON.stringify(data);
console.log('JSON String:', jsonString);
const parsedData = JSON.parse(jsonString);
console.log('Parsed Data:', parsedData);
如果需要保留这些特殊类型,可以采取其他方式进行处理,比如自定义序列化和反序列化方法。
小结
使用 JSON.stringify 和 JSON.parse 要注意以下问题:
- 在使用 JSON.stringify() 时,确保要序列化的对象是可序列化的,即不包含循环引用和特殊类型(如函数)。
- 当使用 JSON.stringify() 进行对象序列化时,确保对象的属性值不包含循环引用,否则会导致无限递归并抛出异常。
- 在使用 JSON.parse() 时,确保要解析的字符串是有效的 JSON 格式,否则会抛出异常。
- 考虑到安全性,避免解析不受信任的 JSON 字符串,因为它们可能包含恶意代码。
- 当需要处理特殊类型(如函数、日期等)时,可以使用自定义的序列化和反序列化方法。