关于 JSON 数据格式的完全使用指南

news2024/12/23 13:36:52

前言

本文将对 JSON 的语法、解析、序列化进行详细的说明,帮助大家掌握 JSON 的使用方式。 如果文中有不对、疑惑或者错字的地方,欢迎在评论区留言指正🌻

一、JSON简介

在 JSON 之前,XML 曾经一度成为互联网上传输数据的事实标准。第一代 Web服务很大程度上是以 XML 为基础的,以服务器间通信为主要特征。可是,XML 也并非没有批评者。有的人认为 XML 过于冗余和啰唆。为解决这些问题,也出现了几种方案。不过 Web 已经朝着它的新方向进发了。

2006年,Douglas Crockford 在国际互联网工程任务组(IETF,The Internet Engineering Task Force)制定了 JavaScript 对象简谱(JSON, JavaScript Object Notation)标准,即 RFC 4627。但实际上,JSON 早在 2001 年就开始使用了。JSON 是 JavaScript 的严格子集,利用 JavaScript 中的几种模式来表示结构化数据。Crockford 将 JSON 作为替代 XML 的一个方案提出,因为 JSON 可以直接传给 eval() 而不需要创建 DOM。

理解 JSON 最关键的一点是要把它当成一种数据格式,而不是编程语言。 JSON 不属于 JavaScript,它们只是拥有相同的语法而已。JSON 也不是只能 在 JavaScript 中使用,它是一种通用数据格式。很多语言都有解析和序列 化 JSON 的内置能力。

二、语法

JSON 语法支持表示 3 种类型的值。

  • 简单值 :字符串、数值、布尔值和 null 可以在 JSON 中出现,就像在 JavaScript 中一样。特殊值 undefined 不可以。* 对象 :第一种复杂数据类型,对象表示有序键/值对。每个值可以是 简单值,也可以是复杂类型。* 数组 :第二种复杂数据类型,数组表示可以通过数值索引访问的值的 有序列表。数组的值可以是任意类型,包括简单值、对象,甚至其他数组。JSON 没有变量、函数或对象实例的概念。JSON 的所有记号都只为表示结构化数据,虽然它借用了 JavaScript 的语法,但是千万不要把它跟 JavaScript 语言混淆。

1. 简单值

最简单的JSON可以是一个数值。例如,下面这个数值是有效的JSON:

5 

这个 JSON 表示数值 5。类似地,下面这个字符串也是有效的 JSON:

"Hello CoderBin!" 

JavaScript 字符串与 JSON 字符串的主要区别是,JSON 字符串必须使用双引号(单引号会导致语法错误)。

布尔值和 null 本身也是有效的 JSON 值。不过,实践中更多使用 JSON 表示比较复杂的数据结构,其中会包含简单值。

2. 对象

对象使用与 JavaScript 对象字面量略为不同的方式表示。以下是 JavaScript 中的对象字面量:

let person = {name: 'CoderBin',age: 18
} 

虽然这对 JavaScript 开发者来说是标准的对象字面量,但 JSON 中的对象必须使用双引号把属性名包围起来。下面的代码与前面的代码是一样的:

let object = {"name": "CoderBin","age" : 18
}; 

而用 JSON 表示相同的对象的语法是:

{"name": "CoderBin","age": 18
 } 

与 JavaScript 对象字面量相比,JSON 主要有两处不同。

  • 没有变量声明( JSON 中没有变量)。
  • 最后没有分号(不需要,因为不是 JavaScript 语句)。

同样,用引号将属性名包围起来才是有效的 JSON。属性的值可以是简单值或复杂数据类型值,后者可以在对象中再嵌入对象,比如:

{"name": "CoderBin","age": 18,"friend": {"name": "Jack","address": "guangzhou"}
} 

这个例子在顶级对象中又嵌入了朋友相关的信息。即使整个 JSON 对象中有 两个属性都叫 “name” ,但它们属于两个不同的对象,因此是允许的。同一个对象中不允许出现两个相同的属性。

与 JavaScript 不同,JSON 中的对象属性名必须始终带双引号。手动编写 JSON 时漏掉这些双引号或使用单引号是常见错误。

3. 数组

JSON的第二种复杂数据类型是数组。数组在 JSON 中使用 JavaScript 的数组 字面量形式表示。例如,以下是一个 JavaScript 数组:

let values = [25, "hi", true]; 

在 JSON 中可以使用类似语法表示相同的数组:

[25, "hi", true] 

同样,这里没有变量,也没有分号。数组和对象可以组合使用,以表示更加复杂的数据结构,比如:

[{ "name": "CoderBin","friends": [ "Jack", "Tom" ],"number": 9,"year": 2022},{ "name": "Jhon","friends": [ "lily", "joey" ],"number": 8,"year": 2022}
] 

前面这个数组包含了很多表示人的对象。每个对象都包含一些键,其中一个是 “authors” ,对应的值也是一个数组。对象和数组通常会作为 JSON 数组的顶级结构(尽管不是必需的),以便创建大型复杂数据结构。

三、解析

JSON 的迅速流行并不仅仅因为其语法与 JavaScript 类似,很大程度上还因为 JSON 可以直接被解析成可用的 JavaScript 对象。与解析为 DOM 文档的 XML 相比,这个优势非常明显。为此,JavaScript 开发者可以非常方便地使用 JSON 数据。比如,前面例子中的 JSON 包含很多人的信息,通过如下代码就可以获取第二个人的姓名:

persons[1].name 

当然,以上代码假设把前面的数据结构保存在了变量persons 中。相比之下,遍历DOM结构就显得麻烦多了:

document.getElementsByTagName("person")[1].getAttribute("name"); 

看看这些方法调用,就不难想象为什么 JSON 大受 JavaScript 开发者欢迎 了。JSON 出现之后就迅速成为了 Web 服务的事实序列化标准。

1. JSON对象

早期的 JSON 解析器基本上就相当于 JavaScript 的 eval() 函数。因为 JSON 是 JavaScript 语法的子集,所以 eval() 可以解析、解释,并将其作为 JavaScript 对象和数组返回。ECMAScript 5 增加了 JSON 全局对象,正式 引入解析 JSON 的能力。这个对象在所有主流浏览器中都得到了支持。考虑到直接执行代码的风险,最好不要在旧版本浏览器中只使用 eval() 求值 JSON。

JSON 对象有两个方法:stringify()parse() 。在简单的情况下,这两个方法分别可以将 JavaScript 序列化为 JSON 字符串,以及将 JSON 解析为 原生 JavaScript 值。例如:

let person = {name: 'CoderBin',friends: ['Jack', 'Tom'],number: 9,year: 2022
}
let jsonText = JSON.stringify(person) 

这个例子使用 JSON.stringify() 把一个 JavaScript 对象序列化为一个 JSON 字符串,保存在变量 jsonText 中。默认情况下,JSON.stringify() 会输出不包含空格或缩进的 JSON 字符串,因此 jsonText 的值是这样的:

{"name":"CoderBin","friends":["Jack","Tom"],"number":9,"year":2022} 

在序列化 JavaScript 对象时,所有函数和原型成员都会有意地在结果中省略。此外,值为 undefined 的任何属性也会被跳过。最终得到的就是所有实例属性均为有效 JSON 数据类型的表示。

JSON 字符串可以直接传给 JSON.parse(),然后得到相应的 JavaScript 值。比如,可以使用以下代码创建与 person 对象类似的新对象:

let personCopy = JSON.parse(jsonText); 

注意,person 和 personCopy 是两个完全不同的对象,没有任何关系。但是它们拥有相同的属性和值。

如果给 JSON.parse() 传入的 JSON 字符串无效,则会导致抛出错误。

四、序列化选项

实际上,JSON.stringify() 方法除了要序列化的对象,还可以接收两个参数。这两个参数可以用于指定其他序列化 JavaScript 对象的方式。

  • 第一个参数是过滤器,可以是数组或函数;
  • 第二个参数是用于缩进结果 JSON 字符串的选项。

单独或组合使用这些参数可以更好地控制 JSON 序列化。

1. 过滤结果

如果第二个参数是一个数组,那么 JSON.stringify() 返回的结果 只会包含该数组中列出的对象属性。比如下面的例子:

let person = {name: 'CoderBin',friends: ['Jack', 'Tom'],number: 9,year: 2022
}
let jsonText = JSON.stringify(person, ["name", "number"]) 

在这个例子中,JSON.stringify() 方法的第二个参数是一个包含 两个字符串的数组:"name""number" 。它们对应着要序列化的对象中的属性,因此结果JSON字符串中只会包含这两个属性:

{"name":"CoderBin","number":9} 

如果第二个参数是一个函数,则行为又有不同。提供的函数接收两个参数:属性名(key )和属性值(value )。可以根据这个 key 决定要对相应属性执行什么操作。这个 key 始终是字符串,只是在值不属于某个键/值对时会是空字符串。为了改变对象的序列化,返回的值就是相应 key 应该包含的结果。注意,返回 undefined 会导致属性被忽略。下面看一个例子:

let person = {name: 'CoderBin',friends: ['Jack', 'Tom'],number: 9,year: 2022
}
let jsonText = JSON.stringify(person, (key, value) => {switch (key) {case 'friends':return value.join(',')case 'number':return 100case 'year':return undefineddefault:return value}
}) 

这个函数基于键进行了过滤。如果键是 "friends" ,则将数组值转 换为字符串;如果键是 "number" ,则将值设置为 100 ;如果键 是 "year" ,则返回 undefined 忽略该属性。最后一定要提供默 认返回值,以便返回其他属性传入的值。第一次调用这个函数实际上会传入空字符串 key ,值是 person 对象。最终得到的 JSON字 符串是这样的:

{"name":"CoderBin","friends":"Jack,Tom","number":100} 

注意:函数过滤器会应用到要序列化的对象所包含的所有对象,因此 如果数组中包含多个具有这些属性的对象,则序列化之后每个对象都 只会剩下上面这些属性。

2. 字符串缩进

JSON.stringify() 方法的第三个参数控制缩进和空格。在这个参数是数值时,表示每一级缩进的空格数。例如,每级缩进4个空格,可以这样:

let person = {name: 'CoderBin',friends: ['Jack', 'Tom'],number: 9,year: 2022
}
let jsonText = JSON.stringify(person, null, 4) 

这样得到的jsonText 格式如下:

{"name": "CoderBin","friends": ["Jack","Tom"],"number": 9,"year": 2022
} 

注意,除了缩进,JSON.stringify() 方法还为方便阅读插入了换行符。这个行为对于所有有效的缩进参数都会发生。(只缩进不换行也没什么用。)最大缩进值为10,大于10的值会自动设置为10。

如果缩进参数是一个字符串而非数值,那么 JSON 字符串中就会使用这个字符串而不是空格来缩进。使用字符串,也可以将缩进字符设置为 Tab 或任意字符,如两个连字符:

let jsonText = JSON.stringify(person, null, '--') 

这样,jsonText 的值会变成如下格式:

{
--"name": "CoderBin",
--"friends": [
----"Jack",
----"Tom"
--],
--"number": 9,
--"year": 2022
} 

使用字符串时同样有10个字符的长度限制。如果字符串长度超过10, 则会在第10个字符处截断。

3. toJSON() 方法

有时候,对象需要在 JSON.stringify() 之上自定义 JSON 序列化。 此时,可以在要序列化的对象中添加 toJSON() 方法,序列化时会基 于这个方法返回适当的 JSON 表示。事实上,原生 Date 对象就有一个 toJSON() 方法,能够自动将 JavaScript 的 Date 对象转换为 ISO 8601 日期字符串(本质上与在 Date 对象上调用 toISOString() 方法一样)。

下面的对象为自定义序列化而添加了一个 toJSON() 方法:

let person = {name: 'CoderBin',friends: ['Jack', 'Tom'],number: 9,year: 2022,toJSON: function() {return this.name}
}

let jsonText = JSON.stringify(person) 

这里 person 对象中定义的 toJSON() 方法简单地返回了姓名 (this.name )。与 Date 对象类似,这个对象会被序列化为简单 字符串而非对象。toJSON() 方法可以返回任意序列化值,都可以起到相应的作用。如果对象被嵌入在另一个对象中,返回 undefined 会导致值变成 null ;或者如果是顶级对象,则本身就是 undefined。

注意:箭头函数不能用来定义 toJSON() 方法。主要原因是箭头函数的词法作用域是全局作用域,在这种情况下不合适。

toJSON() 方法可以与过滤函数一起使用,因此理解不同序列化流程的顺序非常重要。在把对象传给 JSON.stringify() 时会执行如下步骤。

(1) 如果可以获取实际的值,则调用 toJSON() 方法获取实际的值, 否则使用默认的序列化。

(2) 如果提供了第二个参数,则应用过滤。传入过滤函数的值就是第 (1)步返回的值。

(3) 第(2)步返回的每个值都会相应地进行序列化。

(4) 如果提供了第三个参数,则相应地进行缩进。

五、解析选项

JSON.parse() 方法也可以接收一个额外的参数,这个函数会针对每个 键/值对都调用一次。为区别于传给 JSON.stringify() 的起过滤作用的替代函数 (replacer),这个函数被称为还原函数(reviver)。实际上它们的格式完全一样,即还原函数也接收两个参数,属性名(key )和属性值(value ),另外也需要返回值。

如果还原函数返回 undefined,则结果中就会删除相应的键。如果返回了其他任何值,则该值就会成为相应键的值插入到结果中。还原函数经常被用于把日期字符串转换为 Date 对象。例如:

let person = {name: 'CoderBin',friends: ['Jack', 'Tom'],number: 9,year: 2022,releaseDate: new Date(2022, 11, 4)
}

let jsonText = JSON.stringify(person)

const personCopy = JSON.parse(jsonText, (key, value) => {if (key === 'releaseDate') {return new Date(value)} else {return value}
})

console.log(personCopy.releaseDate.getFullYear()) // 2022 

以上代码在 person 对象中增加了 releaseDate 属性,是一个 Date 对象。这个对象在被序列化为 JSON 字符串后,又被重新解析为一个对象 personCopy 。这里的还原函数会查找 "releaseDate" 键,如果找到就会 根据它的日期字符串创建新的 Date 对象。得到的 personCopy.releaseDate 属性又变回了 Date 对象,因此可以调用其 getFullYear() 方法。

六、小结

JSON 是一种轻量级数据格式,可以方便地表示复杂数据结构。这个格式使用 JavaScript 语法的一个子集表示对象、数组、字符串、数值、布尔值和 null 。虽然XML也能胜任同样的角色,但JSON更简洁,JavaScript 支持也更好。更重要的是,所有浏览器都已经原生支持全局 JSON 对象。

ECMAScript 5 定义了原生 JSON 对象,用于将 JavaScript 对象序列化为 JSON 字符串,以及将 JSON 数组解析为 JavaScript 对象。JSON.stringify()JSON.parse() 方法分别用于实现这两种操作。 这两个方法都有一些选项可以用来改变默认的行为,以实现过滤或修改流程。

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/197443.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读】【剪枝】Learning Efficient Convolutional Networks through Network Slimming

摘要 深度卷积神经网络(CNN)在许多实际应用中的部署在很大程度上受到其高计算成本的阻碍。在本文中,我们提出了一种新的神经网络学习方案,以同时1)减小模型大小;2) 减少运行时内存占用&…

Kali Linux- 社会工程及压力工具教程

在本章中,我们将了解 Kali Linux 中使用的社会工程工具。 文章目录社会工程Kali Linux - 压力工具SlowhttptestinvitefloodTHC-SSL-DOS总结社会工程 社会工程师工具包 (SET) 是一个专为社会工程设计的开源渗透测试框架。SET具有许多自定义攻…

第九章(12):STL之常用查找算法

文章目录前情回顾常用查找算法findfind_ifadjacent_findbinary_searchcountcount_if下一座石碑🎉welcome🎉 ✒️博主介绍:一名大一的智能制造专业学生,在学习C/C的路上会越走越远,后面不定期更新有关C/C语法&#xff0…

读懂用户之用户调研怎么做?(内附模板教程)

随着互联网的发展,不管是做产品设计、运营,还是市场推广,我们都需要思考的是“用户真正想要的是什么?”。这时候,用户调研的重要性就凸显出来了。 一、什么是用户调研 用户调研,指通过各种方式得到受访者的…

Redis常用指令

3. 常用指令 在这部分中呢,我们家学习两个知识,第一个是key的常用指令,第二个是数据库的常用指令。和前面我们学数据类型做一下区分,前面你学的那些指令呢,都是针对某一个数据类型操作的,现在学的都是对所…

#14环形链表#

环形链表 1题目链接 链接 2思路 slow和fast指向链表的开始 slow一次走一步 fast一次走两步 不带环 fast就会为空 带环 fast就会在环里追上slow 3实现 bool hasCycle(struct ListNode* head) {struct ListNode* slow head, * fast head;while (fast && fast->ne…

微信小程序学习第3天——网络数据请求

一、小程序网络请求限制 1、必须https类型的接口 2、必须将接口的域名添加到信任列表中 二、配置request合法域名 配置步骤:登录微信小程序管理后台 -> 开发 -> 开发设置 -> 服务器域名 -> 修改 request 合法域名 点击修改request合法域名&#xf…

【自动化测试】从0开始玩转docker—— 01软件安装

目的 CI / CD在目前各类互联网企业中已然成为推动软件开发行为的重要基础设施服务。同样的对于测试团队来说更是有着举足轻重的重大意义,无论是测试左移的具象化提现亦或是持续测试的顺利开展,掌握这一技能已是广大软件测试工程师的必修课。分享这一技术…

Springboot+Vue+Uniapp自媒体视频系统

简介:本项目采用了基本的springbootvueuniapp设计的自媒体系统。详情请看主要截图。经测试,本项目正常运行。本项目适用于Java毕业设计、课程设计学习参考等用途。 项目描述 项目名称SpringbootVueUniapp自媒体视频系统源码作者LHL项目类型Java EE项目 …

C++vector容器

目录 1.vector基本概念 2.构造函数 3.vector赋值操作,,assign 4.vector容量和大小 ,empty,capacity,size,resize 5.vector的插入和删除,push_back,pop_back,insert,erase,clear 6.vector数据存取,at,[],遍历 7.vector互换…

C++之引用(上)

文章目录前言一、引用二、引用的写法三、引用特性1.引用在定义时必须初始化2. 一个变量可以有多个引用3. 引用一旦引用一个实体,再不能引用其他实体三、引用的权限(含例子)四、常引用总结前言 今天要介绍的是C中的一个新概念——引用。 一、…

MobaXterm使用指南

MobaXterm使用指南 1. 介绍 通俗的来讲,MobaXterm就是一款SSH客户端,它帮助我们在Windows操作系统下去连接并操作Linux服务器。MobaXterm 又名 MobaXVT,是一款增强型终端、X 服务器和 Unix 命令集(GNU/ Cygwin)工具箱。MobaXterm 可以开启多…

Django网页+Yolov5垃圾识别系统

Django网页Yolov5垃圾识别系统如需安装运行环境或远程调试&#xff0c;见文章底部个人微信或QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<Django网页Yolov5垃圾识别系统>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易…

代码随想录NO33 |Leetcode 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球

贪心LeetCode_860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球今天是贪心第四天的题了&#xff0c;快开始dp了&#xff01;大头啊&#xff01; 860.柠檬水找零 在柠檬水摊上&#xff0c;每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品&#xff0c;&am…

day28|1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果

1005.K次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数组 可能的…

若依框架实现多级联动下拉

最近也是用到了若依的多级联动&#xff0c;效果如下&#xff08;可多级&#xff09; 若依有已经封装好的一套js&#xff0c;难点在于后端数据封装&#xff0c;前端按照封装好的代码引用即可。 这里主要分享下关于后端数据如何封装多层。后端代码直接奉上。 记得要把集合转j…

SpringBoot+Vue+Wx新冠疫苗预约系统

简介&#xff1a;本项目采用了基本的springbootvue微信小程序设计的新冠疫苗预约系统。详情请看主要截图。经测试&#xff0c;本项目正常运行。本项目适用于Java毕业设计、课程设计学习参考等用途。 项目描述 项目名称SpringBootVueWx新冠疫苗预约系统源码作者LHL项目类型Java…

python爬虫正则表达式

博主简介&#xff1a;博主是一个大二学生&#xff0c;主攻人工智能领域研究。感谢缘分让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;C&#xff0c;python&#xff0c;爬虫等方面的知识分享。如果有需要的小伙伴&#xff0c;可以关注博主&#xff…

零碎的算法笔记(1)

From算法竞赛入门经典 第2版1.判断 n 是否为完全平方数2. 比较大的数组应尽量声明在 main 函数外&#xff0c;否则程序可能无法运行 3.开灯问题1.判断 n 是否为完全平方数 可以先求出其平方根&#xff0c;然后看它是否为整数&#xff0c;即用一个 int 型变量 m 存储 sqrt(n&am…

C++入门(二)

C入门&#xff08;二&#xff09;1.引用1.1引用概念1.2引用特性1.3常应用1.4使用场景1.5传值、传引用效率比较1.6引用和指针的区别2.内联函数2.1概念2.2内联函数的特性3.auto关键字&#xff08;C11&#xff09;3.1auto使用细则4.基于范围的for循环&#xff08;C11&#xff09;5…