likes
comments
collection
share

你不知道的JSON数据交换格式

作者站长头像
站长
· 阅读数 15

JSON的全称是JavaScript Object Notation(JavaScript 对象表示法)。看文字会以为这是以JavaScript为基础的数据格式,而实际上学习JSON前学一点JavaScript肯定会有帮助,因为JSON源于JavaScript的一个子集。

但如果以后用不到JavaScript,也没必要学习它,因为数据交换格式是独立于语言的。JSON也是可以独立于JavaScript存在的,JSON更多的意义在于对象表示法上。

JSON语法

JSON是基于JavaScript对象字面量的。所谓字面量是对数据值的具体表示。它的字面意思与其想要表达的意思完全一致。在编程中,每天都在使用字面量,比如给一个变量赋值

let x = 5

5就是字面量,可以说变量的实际值就是字面量。

JSON所基于的JavaScript对象字面量单纯指对象字面量以及其属性的语法表示,这种属性表示法大家都知道其实就是名称-值对。具体来说,JSON有以下语法特性

  1. JSON是基于JavaScript对象字面量的,但不包括与JavaScript对象字面量的函数相关的部分
  2. 在JSON中的名称-值对中,名称始终被双引号包裹
  3. 在JSON中的名称-值对中,值可以是字符串、数字、布尔值、null、对象或者数组
  4. 在JSON中,多个名称-值对使用逗号分隔
  5. JSON文件的扩展名是.json
  6. JSON的媒体类型为application/json

JSON数据类型

上面讲了JSON的数据类型:值可以是数字、字符串、布尔值、数组、对象和null

需要注意以下几点

  1. JSON中的日期数据类型并不被支持,日期通常以字符串的形式进行传输

  2. JSON中的布尔值:truefalse,只能小写

  3. JSON中的null必须小写

  4. 在JSON字符串中,某些特殊字符必须被转义,即在它们前面加上反斜杠(\)

    1. 双引号("):必须写成 "
    2. 反斜杠(\):必须写成 \
    3. 断行(\n):必须写成 \n
    4. 回车(\r):必须写成 \r
    5. 制表符(\t):必须写成 \t
    6. 退格(\b):必须写成 \b
    7. 换页(\f):必须写成 \f
    8. 单引号('):虽然JSON标准中不需要转义,但为了在某些编程语言中正确解析,通常也会写成 '

    例如,一个包含双引号和断行的JSON字符串应该这样写

    {
      "text": "He said, \"Hello, World!\\nHow are you?\""
    }
    

JSON Schema

JSON Schema是一种用于描述JSON数据格式的语言,它提供了一种清晰、易于理解的界面,可以用于验证、注释以及操作JSON数据。

JSON Schema定义了以下几个方面:

  1. 哪些字段是必需的
  2. 字段的类型(例如:字符串、数字、对象、数组等)
  3. 字段的长度或大小
  4. 字段的格式(例如:日期、邮箱等)
  5. 字段的默认值
  6. 字段的枚举值等

以下是一个简单的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对象。在使用这两个方法时,有一些需要注意的地方

  1. JSON.stringify():

    • 不会序列化函数和undefined。
    • 对象中的循环引用会导致错误。
    • 会忽略对象中不可枚举属性。
    • 如果对象中存在toJSON()方法,JSON.stringify()会调用这个方法,并使用它的返回值作为序列化的结果。
    • 可以接受一个可选的replacer参数,用于过滤或转换结果。
    • 可以接受一个可选的space参数,用于控制结果的缩进。
  2. 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()方法中只返回了namecity属性,所以在序列化时,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()方法相似作用。

总结要点

  1. JSON是基于JavaScript对象字面量的,但不包括与JavaScript对象字面量的函数相关的部分
  2. 在JSON中的名称-值对,名称始终被双引号包裹
  3. 在JSON中的名称-值对,值可以是字符串、数字、布尔值、null、对象或者数组
  4. 在JSON中,多个名称-值对使用逗号分隔
  5. JSON文件的扩展名是.json
  6. JSON的媒体类型为application/json
  7. JSON中的日期数据类型并不被支持,日期通常以字符串的形式进行传输。
  8. JSON中的布尔值:truefalse,只能小写
  9. JSON中的null必须小写
  10. 在JSON字符串中,某些特殊字符必须被转义,即在它们前面加上反斜杠(\)
  11. JSON Schema是一种用于描述JSON数据格式的语言,它提供了一种清晰、易于理解的界面,可以用于验证、注释以及操作JSON数据
  12. 在JavaScript中,可以使用JSON.stringify()方法进行序列化,将JavaScript对象转换为JSON字符串,使用JSON.parse()方法进行反序列化,将JSON字符串转换为JavaScript对象。注意使用它们的注意事项
  13. JSON.stringify()方法的第二个参数可以过滤结果,第三个可以缩进字符串
  14. toJSON()方法的作用和JSON.parse()的接受额外参数作用

本文完。

转载自:https://juejin.cn/post/7283387739734474812
评论
请登录