整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

程序员都应该了解的一种数据格式之 JSON

者 | 猪哥

责编 | maozz

JSON的诞生原因是因为XML整合到HTML中各个浏览器实现的细节不尽相同,所以道格拉斯·克罗克福特(Douglas Crockford) 和 奇普·莫宁斯达(Chip Morningstar)一起从JS的数据类型中提取了一个子集,作为新的数据交换格式,因为主流的浏览器使用了通用的JavaScript引擎组件,所以在解析这种新数据格式时就不存在兼容性问题,于是他们将这种数据格式命名为 “JavaScript Object Notation”,缩写为 JSON,由此JSON便诞生了!

今天我们来学习一下JSON的结构形式、数据类型、使用场景以及注意事项吧!

JSON格式

上面我们知道JSON是从JavaScript的数据类型中提取出来的子集,那JSON有几种结构形式呢?又有哪些数据类型呢?他们又分别对应着JavaScript中的哪些数据类型呢?

JSON的2种结构形式,键值对形式和数组形式。

举了一个JSON的实例,就是键值对形式的,如下:

{

"person": {

"name": "pig",

"age": "18",

"sex": "man",

"hometown": {

"province": "江西省",

"city": "抚州市",

"county": "崇仁县"

}

}

}

这种结构的JSON数据规则是:一个无序的“‘名称/值’对”集合。一个对象以 {左括号 开始, }右括号 结束。每个“名称”后跟一个 :冒号 ;“‘名称/值’ 对”之间使用 ,逗号 分隔。

因为大多数的时候大家用的JSON可能都是上面那种key-value形式,所以很多人在讲解JSON的时候总是会忽略数组形式,这一点是需要注意的。

那JSON的数组形式是怎么样的呢?举一个实例吧!

["pig", 18, "man", "江西省抚州市崇仁县"]

数组形式的JSON数据就是值(value)的有序集合。一个数组以 [左中括号 开始, ]右中括号 结束。值之间使用 ,逗号 分隔。

JOSN的6种数据类型

上面两种JSON形式内部都是包含value的,那JSON的value到底有哪些类型,而且上期我们说JSON其实就是从Js数据格式中提取了一个子集,那具体有哪几种数据类型呢?

  1. string:字符串,必须要用双引号引起来。

  2. number:数值,与JavaScript的number一致,整数(不使用小数点或指数计数法)最多为 15 位,小数的最大位数是 17。

  3. object:JavaScript的对象形式,{ key:value }表示方式,可嵌套。

  4. array:数组,JavaScript的Array表示方式[ value ],可嵌套。

  5. true/false:布尔类型,JavaScript的boolean类型。

  6. :空值,JavaScript的。

以上数据形式图片来源JSON官方文档:http://www.json.org/json-zh.html

JSON使用场景

介绍完JSON的数据格式,那我们来看看JSON在企业中使用的比较多的场景。

接口返回数据和序列化。JSON用的最多的地方莫过于Web了,现在的数据接口基本上都是返回的JSON,具体细化的场景有:

  1. Ajxa异步访问数据

  2. RPC远程调用

  3. 前后端分离后端返回的数据

  4. 开放API,如百度、高德等一些开放接口

  5. 企业间合作接口

这种API接口一般都会提供一个接口文档,说明接口的入参、出参等,

一般的接口返回数据都会封装成JSON格式,比如类似下面这种

{

"code": 1,

"msg": "success",

"data": {

"name": "pig",

"age": "18",

"sex": "man",

"hometown": {

"province": "江西省",

"city": "抚州市",

"county": "崇仁县"

}

}

}

程序在运行时所有的变量都是保存在内存当中的,如果出现程序重启或者机器宕机的情况,那这些数据就丢失了。一般情况运行时变量并不是那么重要丢了就丢了,但有些内存中的数据是需要保存起来供下次程序或者其他程序使用。

保存内存中的数据要么保存在数据库,要么保存直接到文件中,而将内存中的数据变成可保存或可传输的数据的过程叫做序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

正常的序列化是将编程语言中的对象直接转成可保存或可传输的,这样会保存对象的类型信息,而JSON序列化则不会保留对象类型!

为了让大家更直观的感受区别,猪哥用代码做一个测试,大家一目了然

  1. Python对象直接序列化会保存class信息,下次使用loads加载到内存时直接变成Python对象。

  2. JSON对象序列化只保存属性数据,不保留class信息,下次使用loads加载到内存可以直接转成dict对象,当然也可以转为Person对象,但是需要写辅助方法。

对于JSON序列化不能保存class信息的特点,那JSON序列化还有什么用?答案是当然有用,对于不同编程语言序列化读取有用,比如:我用Python爬取数据然后转成对象,现在我需要将它序列化磁盘,然后使用Java语言读取这份数据,这个时候由于跨语言数据类型不同,所以就需要用到JSON序列化。

存在即合理,两种序列化可根据需求自行选择!

最后就是生成Token和配置文件

首先声明Token的形式多种多样,有JSON、字符串、数字等等,只要能满足需求即可,没有规定用哪种形式。

JSON格式的Token最有代表性的莫过于JWT(JSON Web Tokens)。

随着技术的发展,分布式web应用的普及,通过Session管理用户登录状态成本越来越高,因此慢慢发展成为Token的方式做登录身份校验,然后通过Token去取Redis中的缓存的用户信息,随着之后JWT的出现,校验方式更加简单便捷化,无需通过Redis缓存,而是直接根据Token取出保存的用户信息,以及对Token可用性校验,单点登录更为简单。

使用JWT做过app的登录系统,大概的流程就是:

  1. 用户输入用户名密码

  2. app请求登录中心验证用户名密码

  3. 如果验证通过则生成一个Token,其中Token中包含:

  4. 用户的uid、Token过期时间、过期延期时间等,然后返回给app

  5. app获得Token,保存在cookie中,下次请求其他服务则带上

  6. 其他服务获取到Token之后调用登录中心接口验证

  7. 验证通过则响应

JWT登录认证有哪些优势:

  1. 性能好:服务器不需要保存大量的session

  2. 单点登录(登录一个应用,同一个企业的其他应用都可以访问):使用JWT做一个登录中心基本搞定,很容易实现。

  3. 兼容性好:支持移动设备,支持跨程序调用,Cookie 是不允许垮域访问的,而 Token 则不存在这个问题。

  4. 安全性好:因为有签名,所以JWT可以防止被篡改。更多JWT相关知识自行在网上学习,本文不过多介绍!

说实话JSON作为配置文件使用场景并不多,最具代表性的就是npm的package.json包管理配置文件了,下面就是一个npm的package.json配置文件内容。

{

"name": "server", //项目名称

"version": "0.0.0",

"private": true,

"main": "server.js", //项目入口地址,即执行npm后会执行的项目

"scripts": {

"start": "node ./bin/www" ///scripts指定了运行脚本命令的npm命令行缩写

},

"dependencies": {

"cookie-parser": "~1.4.3", //指定项目开发所需的模块

"debug": "~2.6.9",

"express": "~4.16.0",

"http-errors": "~1.6.2",

"jade": "~1.11.0",

"morgan": "~1.9.0"

}

}

但其实JSON并不合适做配置文件,因为它不能写注释、作为配置文件的可读性差等原因。

配置文件的格式有很多种如:toml、yaml、xml、ini等,目前很多地方开始使用yaml作为配置文件格式。

JSON在Python中的使用

最后我们来看看Python中操作JSON的常用方法有哪些,在Python中操作JSON时需要引入json标准库。

import json

类型转换

Python类型转JSON:json.dump

# 1、Python的dict类型转JSON

person_dict = {'name': 'pig', 'age': 18, 'sex': 'man', 'hometown': '江西抚州'}

# indent参数为缩进空格数

person_dict_json = json.dumps(person_dict, indent=4)

print(person_dict_json, '\n')

# 2、Python的列表类型转JSON

person_list = ['pig', 18, 'man', '江西抚州']

person_list_json = json.dumps(person_list)

print(person_list_json, '\n')

# 3、Python的对象类型转JSON

person_obj = Person('pig', 18, 'man', '江西抚州')

# 中间的匿名函数是获得对象所有属性的字典形式

person_obj_json = json.dumps(person_obj, default=lambda obj: obj.__dict__, indent=4)

print(person_obj_json, '\n')

执行结果:

JSON转Python类型:json.loads

# 4、JSON转Python的dict类型

person_json = '{ "name": "pig","age": 18, "sex": "man", "hometown": "江西抚州"}'

person_json_dict = json.loads(person_json)

print(type(person_json_dict), '\n')

# 5、JSON转Python的列表类型

person_json2 = '["pig", 18, "man", "江西抚州"]'

person_json_list = json.loads(person_json2)

print(type(person_json_list), '\n')

# 6、JSON转Python的自定义对象类型

person_json = '{ "name": "pig","age": 18, "sex": "man", "hometown": "江西抚州"}'

# object_hook参数是将dict对象转成自定义对象

person_json_obj = json.loads(person_json, object_hook=lambda d: Person(d['name'], d['age'], d['sex'], d['hometown']))

print(type(person_json_obj), '\n')

执行结果如下:

对应的数据类型

上面我们演示了Python类型与JSON的相互转换,最开始的时候我们讲过JSON有6种数据类型,那这6种数据类型分别对应Python中的哪些数据类型呢?

需要注意的点

JSON的键名和字符串都必须使用双引号引起来,而Python中单引号也可以表示为字符串,所以这是个比较容易犯的错误!

Python类型与JSON相互转换的时候到底是用load/dump还是用loads\dumps?

  • 他们之间有什么区别?

  • 什么时候该加s什么时候不该加s?

这个我们可以通过查看源码找到答案:

不加s的方法入参多了一个fp表示filepath,最后多了一个写入文件的操作。

所以我们在记忆的时候可以这样记忆:

加s表示转成字符串(str),不加s表示转成文件。

Python自定义对象与JSON相互转换的时候需要辅助方法来指明属性与键名的对应关系,如果不指定一个方法则会抛出异常!

相信有些看的仔细的同学会好奇上面使用json.dumps方法将Python类型转JSON的时候,如果出现中文,则会出现:

\u6c5f\u897f\u629a\u5dde

这种东西,这是为什么呢?

原因是:Python 3中的json在做dumps操作时,会将中文转换成unicode编码,并以16进制方式存储,而并不是UTF-8格式!

总结

今天我们学习了JSON的2种形式,切记JSON还有[...]这种形式的。

学习了JSON的6种数据类型他们分别对于Python中的哪些类型。

了解了JSON的一些使用场景以及实际的例子。

还学习了在Python中如何使用JSON以及需要注意的事项。

一个JSON知识点却分两篇长文(近万字)来讲,其重要性不言而喻。因为不管你是做爬虫、还是做数据分析、web、甚至前端、测试、运维,JSON都是你必须要掌握的一个知识点

本文为作者投稿,版权归作者个人所有。

SON(JavaScript Object Notation)是一种通用的、轻量级的数据交换格式,而不是编程语言。由于 XML 过于冗余和啰嗦, 因此,使用 JSON 代替 XML 进行网络传输。

一个 JSON 对象可以被储存在它自己的文件中,这基本上就是一个文本文件,扩展名为 .json

JSON语法

基本上 JSON 对象就是基于 JavaScript 对象,因此,可以把 JavaScript 对象写入 JSON 数据,JSON 可以将JavaScript对象分为三类:

  • 简单值:字符串、数值、布尔值和 nullundefined 除外。
  • 对象:复杂的数据类型,对象表示有序键/值对。
  • 数组:复杂的数据类型,数组表示带有索引的有序列表,可以通过索引访问数组中的值。

而在JSON中,使用如下语法规则:

  • 数据在键/值对中,且由逗号分隔。
  • 花括号保存对象,方括号保存数组。
{
    "code": 1,
    "status": true,
    "msg": "Http Request Success",
    "data": [
        {
            “id”: 2011101010,
            "name": "小赵",
            "age": 29,
            "sex": "男",
            "birthday": “1992-04-09”,
            "address": [
                "北京市通州区万达广场",
                "天津市",
                "河北省邯郸市"
            ]
        },
        {
            “id”: 2011101011,
            "name": "小钱",
            "age": 27,
            "sex": "男",
            "birthday": “1994-05-01”,
            "address": [
                "北京市通州区万达广场",
                "天津市",
                "河北省邯郸市"
            ]
        },
        {
            “id”: 2011101013,
            "name": "小玲",
            "age": 28,
            "sex": "女",
            "birthday": “1993-09-26”,
            "address": [
                "北京市通州区万达广场",
                "天津市",
                "河北省邯郸市"
            ]
        }
    ]
}

注意:JSON 简单值中的字符串必须使用双引号标记,因为使用单引号会导致语法错误。

序列化与解析

ECMAScipt 5 添加了用于JSON解析与序列化的 JSON 对象,该对象只包含两个方法:

  • JSON.stringify():用于将JavaScript简单值、对象或数组序列化为JSON字符串。
  • JSON.parse():用于将JSON格式的字符串解析为原生JavaScript值。

下面详细介绍这两种方法。

序列化

JSON.stringify() 方法将一个JavaScript简单值、对象或数组转换为JSON字符串。如果指定一个 replacer 函数,则可以选择性地替换掉,或者指定的 replacer 的数组,则可选择性地仅包含数组指定的属性。

stringify(value, replacer, space): string;
  • value:将要序列化成一个 JSON 字符串的值。
  • replacer:可选参数,用于转换结果的函数或数组。
  • space:可选参数,控制结果中,每个属性的间距,用于美化输出。
let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
};
let json = JSON.stringify(student);

使用 JSON.stringify() 在默认情况下会输出不包含空格或缩进的 JSON 字符串。如下是输出的 JSON 字符串结果:

{"id":2011101011,"name":"小钱","age":27,"birthday":"1994-05-31T16:00:00.000Z","address":["北京市通州区万达广场","天津市","河北省邯郸市"]}

而在序列化时,会有意地将所有函数和原型对象在结果中省略。

注意:值为 undefined 的任何属性都会被省略。

replacer

如果 replacer 是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。

JSON.stringify() 方法的 replacer 参数用于转换结果。

如果 replacer 是一个数组,那 JSON.stringify() 返回的结果只会包含数组中列出的对象属性。如下所示:

let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
};
let json = JSON.stringify(student, ["name", "address"]);

上面的例子,输出的结果会根据第二个参数传入的数组 ["name", "address"] 来获取对应 nameaddress 属性及它们的值进行序列化:

{"name":"小钱","address":["北京市通州区","浙江省杭州市","河北省邯郸市"]}

如果 replacer 是一个函数,它将有键 key 和值 value 两个参数,且根据函数进行相应的序列化操作,且函数返回值应当是JSON中key对应的value值。 key 始终是字符串,只在值不属于某个key时,才会是空字符串。如下所示:

let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
};
let json = JSON.stringify(student, (key, value) => {
    console.log(typeof value);
    if (key == "address") {
        return value.join(",");
    }
    if (key == "birthday") {
        return undefined;
    }
    return value;    // 一定要提供该默认返回值,以返回其它属性传入的值。
});

最终得到的JSON 字符串是这样的:

{"id":2011101011,"name":"小钱","age":27,"address":"北京市通州区,浙江省杭州市,河北省邯郸市"}

注意:使用 replacer 序列化得到的JSON在使用parse进行解析,就只会得到JSON中的属性。

space

space 参数用于控制缩进和空格。当该参数为数值时,表示每一级缩进的空格数。如下所示:

let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
}
let json = JSON.stringify(student, null, 4);

输出的 JSON 格式如下所示:

{
    "id": 2011101011,
    "name": "小钱",
    "age": 27,
    "birthday": "1994-05-31T16:00:00.000Z",
    "address": [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
}

注意:除了缩进,JSON.stringify() 方法还很方便地插入了换行符。最大缩进值为 10,大于 10 的值自动设置为 10;最小缩进值为 1,小于 1 的值意味着没有缩进字符。

当该参数为字符串时,将指定该字符串为空格;如果没有提供,或者值为 null,将没有空格。若两个连字符:

let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
}
let json = JSON.stringify(student, null, "--");

输出的 JSON 格式如下:

{
--"id": 2011101011,
--"name": "小钱",
--"age": 27,
--"birthday": "1994-05-31T16:00:00.000Z",
--"address": [
----"北京市通州区",
----"浙江省杭州市",
----"河北省邯郸市"
--]
}

也可以使用空格 " " 、制表符 '\t'Tab来设置缩进字符。如下所示:

let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ]
}
let json = JSON.stringify(student, null, '\t');

输出的 JSON 格式如下:

{
	"id": 2011101011,
	"name": "小钱",
	"age": 27,
	"birthday": "1994-05-31T16:00:00.000Z",
	"address": [
		"北京市通州区",
		"浙江省杭州市",
		"河北省邯郸市"
	]
}

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

toJSON()

如果对象之中定义了 toJSON() 方法,JSON.stringify() 就会调用该对象的 toJSON() 方法,覆盖默认序列化行为。如下所示:

let student = {
    id: 2011101011,
    name: "小钱",
    age: 27,
    sex: undefined,
    birthday: new Date(1994, 5, 1),
    address: [
        "北京市通州区",
        "浙江省杭州市",
        "河北省邯郸市"
    ],
    toJSON: function () {
        return {
            name: this.name,
            age: this.age
        };
    }
}
let json = JSON.stringify(student, null, " ");

输出的 JSON 格式如下:

{
 "name": "小钱",
 "age": 27
}

toJSON() 方法可以返回任意序列化值,都可以起到相应的作用。

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

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

  1. 如果定义了 toJSON()方法且能获取到实际值,则调用toJSON()方法,否则使用默认的序列化。
  2. 如果定义了JSON.stringify()方法的replacer函数,则使用replacer函数。传入的值就是第 1 步返回的值。
  3. 第 2 步返回的每个值都会相应地进行序列化。
  4. 如果提供了space 参数,则相应地进行缩进。

理解这个顺序有助于决定是创建 toJSON() 方法,还是使用replacer函数,抑或是两者都用。

解析

JSON.parse() 方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换(操作)。

parse(text, reviver): any;
  • text:要被解析成 JavaScript 值的字符串。
  • reviver:可选参数,用于修改解析生成的原始值。
let json = '{' +
    '"id":2011101011,' +
    '"name":"小钱",' +
    '"age":27,' +
    '"birthday":"1994-05-31T16:00:00.000Z",' +
    '"address":[' +
        '"北京市通州区",' +
        '"浙江省杭州市",' +
        '"河北省邯郸市"' +
    ']' +
'}'
let student = JSON.parse(json);

输出的对象为:

{
  id: 2011101011,
  name: '小钱',
  age: 27,
  birthday: '1994-05-31T16:00:00.000Z',
  address: [ '北京市通州区', '浙江省杭州市', '河北省邯郸市' ]
}

注意:如果给 JSON.parse() 传入的 JSON 字符串无效,则会抛出异常。

let book = {
    title: "Professional JavaScript",
    authors: [
        "Nicholas C. Zakas",
        "Matt Frisbie"
    ],
    edition: 4,
    year: 2017,
    releaseDate: new Date(2017, 11, 1)
};
let jsonText = JSON.stringify(book);
let bookCopy = JSON.parse(jsonText,
(key, value) => key == "releaseDate" ? new Date(value) : value);
alert(bookCopy.releaseDate.getFullYear());

reviver

reviver 参数是一个还原函数,如果指定了还原函数,将解析出的 JavaScript 值经过一次转换后返回最终值。该函数的调用时机在 parse 函数返回之前,并接收 keyvalue 两个参数。如果返回 undefined,结果会删除与 undefined 相对应的 key;而返回其它值,则该值会成为相应键的值并插入到返回结果中。

let json = '{' +
    '"id":2011101011,' +
    '"name":"小钱",' +
    '"age":27,' +
    '"birthday":"1994-05-31T16:00:00.000Z",' +
    '"address":[' +
        '"北京市通州区",' +
        '"浙江省杭州市",' +
        '"河北省邯郸市"' +
    ']' +
'}'
let student = JSON.parse(json, (key, value) => {
    if (key == "birthday") {
        return new Date(value);
    }
    return value;
});

在上面的代码中,JSON.parse() 方法中,定义了一个还原函数,获取 birthday 键并重新创建新的 Date 对象。最后, student.birthday 属性变成了 Date 对象,可以调用有关 Date 类的方法了。

总结

通过上面的简单介绍,JSON 这种数据结构可以很方便地表示复杂的数据结构,如 JavaScript 中的数字、字符串、布尔值、数组、对象和 null 都可以用 JSON 格式表示。且比 XML 更加轻量级。而且,现代浏览器都已经原生支持全局 JSON 对象,并且使用 JSON.stringify()JSON.parse() 方法实现了JavaScript与JSON之间相互转换。

SON: JavaScript Object Notation(JavaScript 对象表示法)

JSON 是存储和交换文本信息的语法。类似 XML。

JSON 比 XML 更小、更快,更易解析。

JSON 实例

{"sites": [{"name":"菜鸟教程" , "url":"www.runoob.com"}, {"name":"google" , "url":"www.google.com"}, {"name":"微博" , "url":"www.weibo.com"}]}

这个 sites 对象是包含 3 个站点记录(对象)的数组。

什么是 JSON ?

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)

  • JSON 是轻量级的文本数据交换格式

  • JSON 独立于语言 *

  • JSON 具有自我描述性,更易理解

*JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。

JSON - 转换为 JavaScript 对象

JSON 文本格式在语法上与创建 JavaScript 对象的代码相同。

由于这种相似性,无需解析器,JavaScript 程序能够使用内建的 eval() 函数,用 JSON 数据来生成原生的 JavaScript 对象。

如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!