Python3中的json模块使⽤详解
1. 概述
JSON (JavaScript Object Notation)是⼀种使⽤⼴泛的轻量数据格式. Python标准库中的json模块提供了JSON数据的处理功能. Python中⼀种⾮常常⽤的基本数据结构就是字典(Dictionary). 它的典型结构如下:
d = {
'a': 123,
'b': {
'x': ['A', 'B', 'C']
}
}
⽽JSON的结构如下:
{
"a": 123,
"b": {
"x": ["A", "B", "C"]
}
}
可以看到, Dictionary和JSON⾮常接近, ⽽Python中的json库提供的主要功能, 也是两者之间的转换.
2. 读取JSON
json.loads⽅法可以将包含了⼀个JSON数据的str, bytes或者bytearray对象, 转化为⼀个Python Dictionary. 它的完型接⼝签名如下:
复制代码代码如下:
json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
2.1 最简单的例⼦
json.loads最基本的使⽤⽅式就是将⼀个包含JSON数据的str传递给这个⽅法:
>>> json.loads('{"a": 123}')
{'a': 123}
注意
在Python中, str值可以放在⼀对单引号中, 也可以放在⼀对双引号中:
>>> 'ABC' == "ABC"
True
所以, 在定义Dictionary的str类型的键和值的时候, 使⽤单引号或者双引号都是合法和等价的:
>>> {"a": 'ABC'} == {'a': "ABC"}
True
但是, 在JSON中, 字符串数据只能放在双引号中, 因⽽json.loads⽅法处理的字符串的JSON内容中, 字符串必须使⽤双引号. 否则就会发⽣解码错误:
>>> json.loads("{'a': 123}")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 354, in loads
return _default_decoder.decode(s)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/decoder.py", line 355, in
raw_decode
obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
如果被处理的Python字符串是包含在双引号中的, 那么JSON中的双引号就需要转义:
>>> json.loads("{\"a\": 123}")
{'a': 123}
2.2 bytes和bytearray数据
对于内容是JSON数据的bytes和bytearray, json.loads⽅法也可以处理:
>>> json.loads('{"a": 123}'.encode('UTF-8'))
{'a': 123}
>>> json.loads(bytearray('{"a": 123}', 'UTF-8'))
{'a': 123}
2.3 编码格式
json.loads的第⼆个参数是encoding没有实际作⽤.
由于Python 3中str类型总是使⽤UTF-8编码, 所以s参数为str类型时, json.loads⽅法⾃动使⽤UTF-8编码. 并且, str不能以BOM 字节开头.
当s参数为bytes或者bytearray时, json.loads⽅法会⾃动判断为UTF-8, UTF-16还是UTF-32编码. 默认也是将其按照UTF-8编码转化为str对象进⾏后续处理.
2.4 数据类型转换
JSON可以表⽰四种主类型数据
1.字符串 string
2.数字 number
3.布尔类 boolean
4.空值 null
以及两结数据结构
1.对象 object
2.数组 array
默认实现中, JSON和Python之间的数据转换对应关系如下表:
JSON Python
object dict
array list
string str
number (int)int
number (real)float
true True
false False
null None
实际转换情况如下例:
>>> json.loads("""
... {
... "obj": {
... "str": "ABC",
... "int": 123,
... "float": -321.89,
... "bool_true": true,
.
.. "bool_false": false,
... "null": null,
... "array": [1, 2, 3]
... }
... }""")
{'obj': {'str': 'ABC', 'int': 123, 'float': -321.89, 'bool_true': True, 'bool_false': False, 'null': None, 'array': [1, 2, 3]}}
对于JSON中数字number类型的数据, 有以下⼏点需要注意:
1.JSON中的实数real number类型的精度不能超过Python中的float类型的精度范围, 否则就有精度损失. 如下例:
python怎么读取json文件>>> json.loads('3.141592653589793238462643383279')
3.141592653589793
2.JSON标准不包括⾮数字NaN, 正⽆穷Infinity和负⽆穷-Infinity, 但是json.loads⽅法默认会将JSON字符串中的NaN, Infinity, -Infinity转化为Python中的float('nan'), float('inf')和float('-inf'). 注意, 这⾥JSON中的NaN, Infinity, -Infinity必须⼤⼩写正确并且拼写完整. 如下例
>>> json.loads('{"inf": Infinity, "nan": NaN, "ninf": -Infinity}')
{'inf': inf, 'nan': nan, 'ninf': -inf}
2.5 ⾃定义JSON对象转换类型
json.loads默认将JSON中的对象数据转化为Dictionary类型, object_hook参数可以⽤来改变构造出的对象.
object_hook接受⼀个函数, 这个函数的输⼊参数为JSON中对象数据转化出的Dictionary对象, 其返回值则为⾃定义的对象. 如下例所⽰:
>>> class MyJSONObj:
... def __init__(self, x):
... self.x = x
...
>>> def my_json_obj_hook(data):
... print('obj_hook data: %s' % data)
... return MyJSONObj(data['x'])
...
>>> result = json.loads('{"x": 123}', object_hook=my_json_obj_hook)
obj_hook data: {'x': 123}
>>> type(result)
<class '__main__.MyJSONObj'>
>>> result.x
123
当JSON中的对象有嵌套时, json.loads⽅法会按照深度优先的⽅式遍历对象树, 将各层的对象数据传递给object_hook. 叶节点的JSON对象构造出的Python对象, 会作为⽗节点的⼀个值, 传递给⽗节点的object_hook⽅法. 如下例:
>>> class MyJSONObj:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> def my_json_obj_hook(data):
... print('obj_hook data: %s' % data)
... return MyJSONObj(**data)
...
>>> result = json.loads('{"x": {"x": 11, "y": 12}, "y": {"x": 21, "y":22}}', object_hook=my_json_obj_hook)
obj_hook data: {'x': 11, 'y': 12}
obj_hook data: {'x': 21, 'y': 22}
obj_hook data: {'x': <__main__.MyJSONObj object at 0x10417ef28>, 'y': <__main__.MyJSONObj object at 0x10417ed68>}
除了object_hook参数以外, 还有⼀个object_pairs_hook参数. 这个参数同样可以⽤来改变json.loads⽅法构造出的Python对象的类型. 这个参数和object_hook的不同, 在于传⼊的⽅法所接收到的输⼊数据不是⼀个Dictionary, ⽽是⼀个包含tuple的list. 每个tuple都有两个元素, 第⼀个元素是JSON数据中的键, 第⼆个元素是这个键对应的值. 如JSON对象
{
"a": 123,
"b": "ABC"
}
对应的输⼊数据是
[
('a': 123),
('b', 'ABC')
]
当调⽤json.loads⽅法时, 同时指定object_hook和object_pairs_hook, object_pairs_hook会覆盖object_hook参数.
2.6 ⾃定义JSON数字转换类型
默认实现中, JSON中的实数被转换为Python的float类型, 整数被转换为int或者long类型. 类似object_hook, 我们可以通过parse_float和parse_int参数指定⾃定义的转换逻辑. 这两个⽅法的输⼊参数为表⽰JSON实数或者整数的字符串. 下例中, 我们将实数转换为numpy.float64, 将整数转换为numpy.int64:
>>> def my_parse_float(f):
... print('%s(%s)' % (type(f), f))
... return numpy.float64(f)
...
>>> def my_parse_int(i):
... print('%s(%s)' % (type(i), i))
... return numpy.int64(i)
...
>>> result = json.loads('{"i": 123, "f": 321.45}', parse_float=my_parse_float, parse_int=my_parse_int)
<type 'str'>(123)
<type 'str'>(321.45)
>>> type(result['i'])
<type 'numpy.int64'>
>>> type(result['f'])
<type 'numpy.float64'>
2.6.1 ⾃定义NaN, Infinity和-Infinity转换类型
由于标准JSON数据不⽀持NaN, Infinity和-Infinity, 所以parse_float并不会接收到这⼏个值. 当需要⾃定义这⼏个值转换的对象的时候, 就需要使⽤另外⼀个接⼝parse_constant. ⽐如下例中, 将这⼏个值同样转换为numpy.float64类型:
>>> def my_parse_constant(data):
... print('%s(%s)' % (type(data), data))
... return numpy.float64(data)
...
>>> result = json.loads('{"inf": Infinity, "nan": NaN, "ninf": -Infinity}', parse_constant=my_parse_consta
nt)
<type 'str'>(Infinity)
<type 'str'>(NaN)
<type 'str'>(-Infinity)
>>> result['inf']
inf
>>> type(result['inf'])
<type 'numpy.float64'>
2.7 ⾮对象顶级值
根据JSON规范, ⼀个JSON数据中, 可以只包含⼀个值, ⽽不是⼀个完整的对象. 这个值可以是⼀个字符串, ⼀个数字, 布尔值, 空值, 或者⼀个数组. 除了这三种JSON规范中给出的类型, 还可以是NaN, Infinity或者-Infinity:
>>> json.loads('"hello"')
'hello'
>>> json.loads('123')
123
>>> json.loads('123.34')
123.34
>>> json.loads('true')
True
>>> json.loads('false')
False
>>> print(json.loads('null'))
None
>>> json.loads('[1, 2, 3]')
[1, 2, 3]
2.8 重复键名
在同⼀层级JSON对象中, 不应当出现重复的键名, 不过JSON规范中没有给出这种情况的处理标准. 在json.loads中, 当JSON数据中有重复键名, 则后⾯的键值会覆盖前⾯的:
>>> json.loads('{"a": 123, "b": "ABC", "a": 321}')
{'a': 321, 'b': 'ABC'}
2.9 处理JSON数据⽂件
当JSON数据是保存在⼀个⽂件中的时候, json.load⽅法可以⽤来从这个⽂件中读取数据, 并转换为Python对象. json.load⽅法的第⼀个参数就是指向JSON数据⽂件的⽂件类型对象.
⽐如/tmp/data.json⽂件的内含如下:
{
"a": 123,
"b": "ABC"
}
可以使⽤下例中的代码来读取并转化⽂件中的JSON数据:
>>> with open('/tmp/data.json') as jf:
... json.load(jf)
...
{u'a': 123, u'b': u'ABC'}
除了⽂件类型的对象, 只要是实现了read⽅法的类⽂件对象, 都可以作为fp参数, ⽐如下例中的io.StringIO:
>>> sio = io.StringIO('{"a": 123}')
>>> json.load(sio)
{'a': 123}
json.load⽅法的其他参数的意义和使⽤⽅法和上⽂中的json.loads相同, 这⾥不再赘述.
3 ⽣成JSON
json.dumps⽅法可以将Python对象转换为⼀个表⽰JONS数据的字符串. 它的完整接⼝签名如下:
复制代码代码如下:
json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
它的第⼀个参数obj即为要转换的数据对象.
>>> json.dumps({'a': 123, 'b': 'ABC'})
'{"a": 123, "b": "ABC"}'
3.1 编码格式
json.dumps的ensure_ascii参数⽤来控制⽣成的JSON字符串的编码. 其默认值为True, 此时, 所有的⾮ASCII码字条都会转义.如果不希望⾃动进⾏转义, 则会保持原有编码, 限UTF-8. 如下例所⽰:
>>> json.dumps({'数字': 123, '字符': '⼀⼆三'})
'{"\\u6570\\u5b57": 123, "\\u5b57\\u7b26": "\\u4e00\\u4e8c\\u4e09"}'
>>> json.dumps({'数字': 123, '字符': '⼀⼆三'}, ensure_ascii=False)
'{"数字": 123, "字符": "⼀⼆三"}'
3.2 数据类型转换
在默认实现中, json.dumps可以处理的Python对象, 及其所有的属性值, 类型必须为dict, list, tuple, str, float或者int. 这些类型与JSON的数据转换关系如下表:
Python JSON
dict object
list, tuple array
str string
int, float, int-&float-derived emuns number
True true
False false
None null
实际转换情况如下⽰例:
>>> json.dumps(
... {
... 'str': 'ABC',
... 'int': 123,
... 'float': 321.45,
.
.. 'bool_true': True,
... 'bool_false': False,
... 'none': None,
... 'list': [1, 2, 3],
... 'tuple': [12, 34]
... }
... )
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论