引言
你一定听说过 JSON 吧。JSON 是当前最常用的数据传输格式之一,纯文本,容易使用,方便阅读,最重要的是在多个场合都被大量被使用。
既然 JSON 这么好,那就让我们继续探险,去掌握 python 中对 JSON 的常用操作吧, okay, let's go!
基础知识
庄子曰:“水之积也不厚,则其负大舟也无力。”。要完全掌握一个知识点,先将这个知识点需要的基础知识补齐,这样学的才能牢固。
下面就是我认为学习对 JSON 操作前的知识点。如果您对这部分已经了然于胸,尽可以略过这部分,跳到下一节。
什么是 JSON?
Json (Javascript Object Notation) 是一种轻量级的数据交换格式,它基于 Javascript 的对象字面量。尽管它只是 Javascript 的一个子集,但它与语言无关。以现代编程语言编写的程序,都可以用它来彼此交换数据。它是一种文本格式,人和机器都可以阅读它。
—— 《Javascript 语言精粹》
既然以现代编程语言都可以用它来交换数据,强大的 python 当然也不例外。要更好的使用 JSON 一定要先了解下它的语法。
JSON 的语法
JSON 的值分为 6 种类型,分别是对象,数组,字符串,数字,布尔值 (true
和 false
)和null
。来看一个典型的 JSON 集合,体会下这些类型。
{
"obj": {
"name": "xxx",
"address": {
"country": "china",
"city": "TianJin"
}
},
"arr_simple": [1, 2, 3, 5],
"arr_complex": [
1,
"a",
{
"b": "yyy"
},
true,
null
],
"str": "I am a string",
"num": 888,
"booValue": false,
"nullValue": null
}
看上面代码, JSON 语法有什么特点呢?
- JSON 字符串必须使用 双引号包围
- 可以在任何值前后插入空白(包括空格,制表符,回车,换行),当然这些空白符也可以去除。
像字符串,数字,布尔值,null 都比较简单,无需细数,接下来我们重点来看下对象和数组。
JSON 对象有哪些特点?
JSON 对象的结构是什么样子呢?
上面代码中的 obj
就是一个 JSON 对象,我们来观察下它。
- 它是用
{}
括起来的一个集合,每一项都包含名称 和值 ,如name
就是名称,而值就是xxx
。 - 名称可以是任意字符串,但必须是字符串才可以哦。
- 值只要是上面 6 种类型之一就可以
- 名称/值 对没有固定的顺序,可以是任意顺序
- 可以支持无限层的嵌套,如
obj
对象中嵌套了一个address
对象,但是为了保证处理的高效性,请尽量保持结构的扁平性,也就是不要嵌套太多层哦)
为了能够处理 JSON 数据,许多语言都有对应的数据类型可以映射为 JSON 对象,那么 python 中是什么数据类型呢?
是 dict
,如果有您对 dict
有些遗忘了,就请到这里复习下吧。
JSON 数组有哪些特点?
上面代码中的 arr_simple
和 arr_complex
都表示数组,它们有哪些特点呢?
- 是一个 有序序列
- 只有 值 组成
- 值可以是任意类型的 JSON 值,如
arr_complex
数组。
python 也有能够映射为 JSON 对象的数据类型,是 list
和 tuple
, 如果您对 list
和 tuple
的特性有些生疏了,也可以在这里回顾下。
什么是编码和解码?
说到 JSON 和 python 之间的转换,就会涉及到两个名词:编码 和 解码。
那么到底什么是编码和解码呢?
编码是信息从一种形式或格式转换为另一种形式的过程。解码,是编码的逆过程,亦即把编码过的信息恢复成原来样式。
——*
编码的作用则是为了利于传输和存储,JSON 当然是非常适合的。因此,
- 把 python 对象转换成 JSON 的过程就称为编码
- 把 JSON 转换成 python 对象的过程就称为解码
常用的 json 操作有哪些?
刚开始接触 json 的操作,我主要有下面几个疑问:
- json 操作需要什么库?
- 如何将 python 对象转换成 JSON字符串,更进一步,能不能直接转换成文件句柄存储到文件中?(编码)
- 如何将 json 字符串转换成 python 对象,更进一步,能不能直接将 JSON 格式的文件转换成 python 对象?(解码)
下面就让我们一一来探索这些问题。
json 操作需要什么库?
使用 json 函数前需要先导入 json 库:
import json
json
库本身就是 python 内置的标准库,因此你不需要做任何安装的操作。只要声明了上面语句,就可直接使用。
如何将 python 编码成 JSON?
python 编码为 JSON 的对照表
要完成这个功能,先要看下 python 数据结构编码为 json 的对照表。
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int- & float-derived Enums | number |
True | true |
False | false |
None | null |
有了这张表,我们就可以清楚的知道 python 对象将编码成的 json 格式。
json.dumps()
json.dumps() 方法的作用就是将 python 对象转换成 JSON 字符串,下面来看具体的函数声明:
json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw)
参数看起来好多啊,不过不用担心,这么多参数,只有第一个参数是必填的。下面就来一一了解下这些参数的意义
- obj 就是要编码的 python 对象
-
skipkeys 默认值是
False
。设置为True
,假如obj
中的 dict keys 不是基本类型(str
,int
,float
,bool
,None
), 就会被忽略,而不是抛出TypeError
错误 -
ensure_ascii 默认是
True
, 表示默认使用ascii 编码。如果 obj 内含有非 ASCII 字符,就会出现 "\uXXXX" 格式显式的数据, 设置成False
就会使用字符本来的编码。- 这里要注意,如果输入是中文,需要指定
ensure_ascii=False
- 这里要注意,如果输入是中文,需要指定
-
check_circular 默认值是
True
,如果设置为False
就不会检查内部类型是否包含循环引用,而且循环引用会导致 OverflowError -
allow_nan 默认值为
False
,如果碰到超过范围的float
值(nan
,inf
,-inf
)就使用 (NaN
,Infinity
,-Infinity
) 替换- 如果为
True
碰到这些值则会导致ValueError
- 如果为
-
indent 缩进设置
- 如果是非负整数或者 string, JSON Array 元素和对象元素将会按照设置的缩进格式化显示
- 值为 0, 负值,或者
""
只会插入新的一行 - 值为
None
(也是默认值)会尽可能的压缩
-
separators 分隔符。
- 如果要设置它,参数需要是一个元组
(item_separator, key_separator)
- 默认值是
(', ', ': ')
,表示 keys 之间用,
隔开,而 key 和 value 之间用:
隔开
- 如果要设置它,参数需要是一个元组
-
sort_keys 默认值是
False
,如果设置成True
, dict 结构的输出就会按照 key 来排序 - encoding 默认值是 UTF-8 用于设置 JSON 数据的编码方式,在处理中文时这里一定要注意。
来看一个例子
>>> import json
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> print(json.dumps("\"foo\bar"))
"\"foo\bar"
>>> print(json.dumps('\u1234'))
"\u1234"
>>> print(json.dumps('\\'))
"\\"
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
{"a": 0, "b": 0, "c": 0}
json.dump()
json.dump()
函数的作用就是将 python 对象转换成 JSON 字符串,并将其通过 fp 文件流写入到文件中。来看下具体的函数声明:
json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
和前面的 dumps 函数进行比较,会发现两个函数的参数是非常相似的,而且它们的意义也都相同。来看下面的例子
>>> import json
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump(['streaming API'], io)
>>> io.getvalue()
'["streaming API"]'
如何将 JSON 解码成 python 对象?
JSON 解码为 python 的对照表
要完成这个功能,也先要看下 json 解码为 python 对象的对照表
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int) | int |
number (real) | float |
true | True |
false | False |
null | None |
编码对照表和解码对照表并不是一一对应的,因此如果一个 python对象 先编码成 JSON,再转码回来后得到的对象可就不一定完全相等了。
json.loads()
这个方法的作用就是将参数 s
按照上面的对照表反序列化为一个 python 对象。参数 s
可以是 str
,byte
或者byteArray
格式, 但必须要包含 JSON 文本才可以)。具体函数声明如下:
json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
下面就来一一了解下一些常用参数的意义
- s 就是要解码的 python 字符串
- encoding 指定编码格式
- parse_float ,默认情况下相当于 float(num_str)。如果设置为其他值,将会把一个 JSON 字符串按照 float 解码调用,
- parse_int ,默认情况下相当于 int(num_str),如果指定,将把每个 JSON 字符串按照 int 解码调用
来看下面的例子,其中最后一行就指定了 parse_float
。
>>> import json
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]
>>> json.loads('"\\"foo\\bar"')
'"foo\x08ar'
>>> import decimal
>>> json.loads('1.1', parse_float=decimal.Decimal)
Decimal('1.1')
json.load()
先来看函数声明
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
作用是将 fp
文件流反序列化为 python 对象,其中的参数意义和 loads
方法相同。来看一个例子。
>>> import json
>>> from io import StringIO
>>> io = StringIO('["streaming API"]')
>>> json.load(io)
['streaming API']
结语
本文主要介绍了 JSON 的定义,语法以及 JSON 的常用操作。但是并没有涉及 JSON 处理自定义数据类型的高级内容(JSONEncoder
和JSONDecoder
),这部分内容会再后面的篇章中专门介绍。
下篇会介绍 python 的模块,敬请期待。