你不知道的JSON数据交换格式
JSON的全称是JavaScript Object Notation(JavaScript 对象表示法)。看文字会以为这是以JavaScript为基础的数据格式,而实际上学习JSON前学一点JavaScript肯定会有帮助,因为JSON源于JavaScript的一个子集。
但如果以后用不到JavaScript,也没必要学习它,因为数据交换格式是独立于语言的。JSON也是可以独立于JavaScript存在的,JSON更多的意义在于对象表示法上。
JSON语法
JSON是基于JavaScript对象字面量的。所谓字面量是对数据值的具体表示。它的字面意思与其想要表达的意思完全一致。在编程中,每天都在使用字面量,比如给一个变量赋值
let x = 5
5就是字面量,可以说变量的实际值就是字面量。
JSON所基于的JavaScript对象字面量单纯指对象字面量以及其属性的语法表示,这种属性表示法大家都知道其实就是名称-值对。具体来说,JSON有以下语法特性
- JSON是基于JavaScript对象字面量的,但不包括与JavaScript对象字面量的函数相关的部分
- 在JSON中的名称-值对中,名称始终被双引号包裹
- 在JSON中的名称-值对中,值可以是字符串、数字、布尔值、
null
、对象或者数组 - 在JSON中,多个名称-值对使用逗号分隔
- JSON文件的扩展名是
.json
- JSON的媒体类型为
application/json
JSON数据类型
上面讲了JSON的数据类型:值可以是数字、字符串、布尔值、数组、对象和null
需要注意以下几点
-
JSON中的日期数据类型并不被支持,日期通常以字符串的形式进行传输
-
JSON中的布尔值:
true
和false
,只能小写 -
JSON中的
null
必须小写 -
在JSON字符串中,某些特殊字符必须被转义,即在它们前面加上反斜杠(\)
- 双引号("):必须写成 "
- 反斜杠(\):必须写成 \
- 断行(\n):必须写成 \n
- 回车(\r):必须写成 \r
- 制表符(\t):必须写成 \t
- 退格(\b):必须写成 \b
- 换页(\f):必须写成 \f
- 单引号('):虽然JSON标准中不需要转义,但为了在某些编程语言中正确解析,通常也会写成 '
例如,一个包含双引号和断行的JSON字符串应该这样写
{ "text": "He said, \"Hello, World!\\nHow are you?\"" }
JSON Schema
JSON Schema是一种用于描述JSON数据格式的语言,它提供了一种清晰、易于理解的界面,可以用于验证、注释以及操作JSON数据。
JSON Schema定义了以下几个方面:
- 哪些字段是必需的
- 字段的类型(例如:字符串、数字、对象、数组等)
- 字段的长度或大小
- 字段的格式(例如:日期、邮箱等)
- 字段的默认值
- 字段的枚举值等
以下是一个简单的JSON Schema,它描述了一个包含"name"和"age"两个字段的对象,其中"name"字段是字符串类型且是必需的,"age"字段是整数类型
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"required": ["name"]
}
使用JSON Schema,可以有效地验证JSON数据是否符合预期的格式,从而确保数据的质量和一致性。
JavaScript中使用JSON Schema
在JavaScript中使用ajv
库来进行JSON Schema验证
可以在的HTML文件中引入ajv
库
<script src="https://cdn.jsdelivr.net/npm/ajv@6.12.6/dist/ajv.min.js"></script>
也可以使用npm在项目中安装使用
npm install ajv
然后使用以下代码进行JSON Schema验证
// 创建一个Ajv实例
let ajv = new Ajv();
// 定义JSON Schema
let schema = {
type: 'object',
properties: {
name: {
type: 'string'
},
age: {
type: 'integer'
}
},
required: ['name']
};
// 定义数据
let data = {
name: 'John',
age: 30
};
// 使用ajv进行验证
let validate = ajv.compile(schema);
let valid = validate(data);
if (valid) {
console.log('Data is valid!');
} else {
console.log('Data is invalid: ', validate.errors);
}
例子中如果数据不符合schema(例如,如果"name"字段缺失,或者"age"字段不是一个整数),那么validate.errors
将包含错误的详细信息。
JSON 序列化和反序列化
在JavaScript中,可以使用JSON.stringify()
方法将对象转换为JSON字符串,也就是序列化。使用JSON.parse()
方法进行反序列化,将JSON字符串转换为JavaScript对象。在使用这两个方法时,有一些需要注意的地方
-
JSON.stringify():
- 不会序列化函数和undefined。
- 对象中的循环引用会导致错误。
- 会忽略对象中不可枚举属性。
- 如果对象中存在toJSON()方法,JSON.stringify()会调用这个方法,并使用它的返回值作为序列化的结果。
- 可以接受一个可选的replacer参数,用于过滤或转换结果。
- 可以接受一个可选的space参数,用于控制结果的缩进。
-
JSON.parse():
- 只能解析合法的JSON字符串,如果字符串不符合JSON格式,会抛出错误。
- 可以接受一个可选的reviver参数,用于转换结果的值。
例如
let obj = {
name: 'John',
age: 30,
toJSON: function() {
return this.name;
}
};
let str = JSON.stringify(obj); // "John"
let obj2 = JSON.parse(str); // "John"
例子中由于obj对象中存在toJSON()
方法,所以JSON.stringify()
使用这个方法的返回值作为序列化的结果。然后,JSON.parse()
将这个字符串解析为一个JavaScript值。
JSON.stringify()
过滤结果
JSON.stringify()
的第二个参数可以通过函数或者数组来过滤结果。下面是函数示例
const obj = {
name: 'John',
age: 30,
email: 'john@example.com',
password: 'secret'
};
const filteredResult = JSON.stringify(obj, (key, value) => {
if (key === 'password') {
return undefined; // 过滤掉名为'password'的属性
}
return value;
});
console.log(filteredResult);
输出结果是一个过滤后的JSON字符串
{"name":"John","age":30,"email":"john@example.com"}
如果replacer
函数返回undefined
,则表示过滤掉该属性。如果返回其他值,则表示保留该属性的值。
JSON.stringify()
的第二个参数replacer
也可以是一个数组,用于指定要保留的属性
const obj = {
name: 'John',
age: 30,
email: 'john@example.com',
password: 'secret'
};
const filteredResult = JSON.stringify(obj, ['name', 'age', 'email']);
console.log(filteredResult);
输出结果是一个只包含指定属性的JSON字符串
{"name":"John","age":30,"email":"john@example.com"}
如果数组中的属性不存在于对象中,那么在结果中将不会包含该属性。如果数组为空,则结果将为空对象{}
。
JSON.stringify()
字符串缩进
当使用JSON.stringify()
的第三个参数space
时,可以指定缩进字符串的格式
const obj = {
name: 'John',
age: 30,
email: 'john@example.com'
};
const indentedResult = JSON.stringify(obj, null, 2);
console.log(indentedResult);
定义一个对象obj
,包含了一些属性。通过传递null
作为replacer
参数,表示不进行属性过滤。然后将2
作为space
参数,表示使用两个空格进行缩进。
输出结果是一个缩进的JSON字符串
{
"name": "John",
"age": 30,
"email": "john@example.com"
}
space
参数也可以是一个字符串,用于指定自定义的缩进格式,例如使用制表符或多个空格。
如果space
参数为一个非数字且长度超过10个字符,或者为一个数字且小于0,那么结果将不会进行缩进,而是以紧凑的形式输出。
JSON.stringify()
的toJSON()方法
toJSON()
方法是一个在JSON.stringify()
方法调用时被自动执行的方法。如果一个对象有toJSON()
方法,那么在JSON.stringify()
方法执行时,会首先调用这个对象的toJSON()
方法,并使用它的返回值作为序列化的结果。
toJSON()
方法的主要目的是自定义JSON序列化结果。例如,可能有一个包含复杂数据结构的对象,但只想序列化某些特定的部分
let obj = {
name: 'John',
age: 30,
city: 'New York',
toJSON: function() {
// 只序列化name和city属性
return {
name: this.name,
city: this.city
};
}
};
let jsonString = JSON.stringify(obj);
// jsonString现在是:'{"name":"John","city":"New York"}'
在这个例子中,虽然obj对象有三个属性(name、age和city),但由于在toJSON()
方法中只返回了name
和city
属性,所以在序列化时,age
属性被忽略了。
JSON.parse()的reviver参数
一个例子
JSON.parse('{"p": 5}', function (k, v) {
if (k === "") return v; // 如果到了最顶层,则直接返回属性值,
return v * 2; // 否则将属性值变为原来的 2 倍。
}); // { p: 10 }
JSON.parse('{"1": 1, "2": 2,"3": {"4": 4, "5": {"6": 6}}}', function (k, v) {
console.log(k); // 输出当前的属性名,从而得知遍历顺序是从内向外的,
// 最后一个属性名会是个空字符串。
return v; // 返回原始属性值,相当于没有传递 reviver 参数。
});
// 1
// 2
// 4
// 6
// 5
// 3
// ""
JSON.parse()
接受额外参数,返回的值被拦截了。具有toJSON()
方法相似作用。
总结要点
- JSON是基于JavaScript对象字面量的,但不包括与JavaScript对象字面量的函数相关的部分
- 在JSON中的名称-值对,名称始终被双引号包裹
- 在JSON中的名称-值对,值可以是字符串、数字、布尔值、
null
、对象或者数组 - 在JSON中,多个名称-值对使用逗号分隔
- JSON文件的扩展名是
.json
- JSON的媒体类型为
application/json
- JSON中的日期数据类型并不被支持,日期通常以字符串的形式进行传输。
- JSON中的布尔值:
true
和false
,只能小写 - JSON中的
null
必须小写 - 在JSON字符串中,某些特殊字符必须被转义,即在它们前面加上反斜杠(\)
- JSON Schema是一种用于描述JSON数据格式的语言,它提供了一种清晰、易于理解的界面,可以用于验证、注释以及操作JSON数据
- 在JavaScript中,可以使用
JSON.stringify()
方法进行序列化,将JavaScript对象转换为JSON字符串,使用JSON.parse()
方法进行反序列化,将JSON字符串转换为JavaScript对象。注意使用它们的注意事项 JSON.stringify()
方法的第二个参数可以过滤结果,第三个可以缩进字符串toJSON()
方法的作用和JSON.parse()
的接受额外参数作用
本文完。
转载自:https://juejin.cn/post/7283387739734474812