今天带大家学学 Python 中操纵 JSON 的知识。学完本文,你可以学到如下内容:
1、JSON 是什么?
2、JSON 与 XML 的优劣差异?
3、将 Python 对象编码成 JSON 字符串
4、将已编码的 JSON 字符串解码为 Python 对象
5、解决 JSON 中文乱码问题

JSON 是什么?

JSON 的全称是 JavaScript Object Notation,是一种轻量级的数据交换格式。最初,JSON 只是 JavaScript 的子集,但由于其简单易用而迅速走红。
现今大部分编程语言都支持对 JSON 的解析与生成,而近些年异军突起的 NoSQL 数据库也多参照 JSON 来设计数据存储格式,例如 Mongodb 的BSON(Binary JSON)。
JSON 有以下六种数据类型:number、boolean、string、null、array、object。前三种很好理解,第四个 null 对应 Python 的 None,最后两种,对应 Python 的列表和字典。
{
  "name": "小明",
  "age": 14,
  "gender": true,
  "grade": null,
  "skills": [
    "JavaScript",
    "Java",
    "Python"
  ]
}

JSON 与XML 的优劣差异?

在 JSON 出现之前,人们用 XML 在网络上交换数据,在 JSON 出现后,它基本上就取代了 XML 的位置。两者的共同之处显而易见,它们都是结构化的语言,都可以用于网络数据的交换。
两者最大的差异在于它们的“出身”不同,也就是它们被创造的目的不同。
XML 是 W3C(万维网联盟)发布的可扩展标记语言(Extensible Markup Language),最初设计来弥补 HTML 的不足,以强大的扩展性满足网络信息发布的需要,与它“同级”的有:XHTML\CSS\ECMAScript等。
它包含 DTD、XSD、XPath、XSL 等一大堆复杂的规范,在数据存储、扩展及高级检索等方面都有作用。后来被用于网络数据交换,颇有点大材小用的意思,虽然可胜任,却也有点复杂和冗余。
而 JSON 是 ECMAScript 标准的子集,设计之初就是为了克服 XML 在数据交换上的劣势,所以一方面,它像 XML 一样具有简洁而清晰的层次结构,另一方面,它比 XML 小巧精致,更加适用于网络数据的传输。
JSON 也不是没有缺点,当结构层级很多的时候,它会让人陷入繁琐复杂的数据节点查找中,在可读性上要比 XML 差。

将 Python 对象编码成 JSON 字符串

将 Python 的对象转化为字符串,这个过程也称为序列化,与之相对,将 JSON 字符串转化为 Python 对象,这个过程被称为反序列化。
序列化格式如下,json.dumps() 把 Python 对象序列化,json.dump() 先序列化,然后将内容存入文件:
  • 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)
  • 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)
In [1]: import json
In [2]: d = dict(name='Tom', age='8', score=88)
In [3]: json.dumps(d)
Out[3]: '{"name": "Tom", "age": "8", "score": 88}'
In [4]: with open('test.json', 'w') as f:
   ...:     json.dump(d, f)
用的比较多的参数有:
  • ensure_ascii=True 设置是否编码为ASCII,默认是,若False,则使用原编码格式
  • indent=None 设置打印时缩进,默认不缩进
  • separators=None 设置分隔符,取值是(item_separator, dict_separator)元组,默认为(‘,’,’:’),这表示keys之间用“,”隔开,而key和value之间用“:”隔开
  • sort_keys=False 设置按key值排序,默认不排序
In [15]: d = dict(name='Python猫', age='8', score=88)

In [16]: json.dumps(d)
Out[16]: '{"name": "Python\\u732b", "age": "8", "score": 88}'

In [17]: json.dumps(d, ensure_ascii=False, indent=4, sort_keys=True)
Out[17]: '{\n    "age": "8",\n    "name": "Python猫",\n    "score": 88\n}'

In [18]: print(json.dumps(d, ensure_ascii=False, indent=4, sort_keys=True))
{
    "age": "8",
    "name": "Python猫",
    "score": 88
}

将已编码的 JSON 字符串解码为 Python 对象

反序列化格式如下,json.loads() 从内存中读取内容解析,json.load() 从文件中读取内容解析:
  • json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
  • json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
In [1]: import json
In [2]: d = dict(name='Tom', age='8', score=88)
In [3]: tom_json = json.dumps(d)
In [4]: json.loads(tom_json)
Out[4]: {'age': '8', 'name': 'Tom', 'score': 88}
In [5]: with open('test.json', 'r') as f:
   ...:     print(json.load(f))
{'name': 'Tom', 'age': '8', 'score': 88}
json.loads() 比 json.load() 多了一个 encoding 参数,可以将传入的字符串重新编码。

解决中文乱码问题

序列化的 ensure_ascii 参数与反序列化的 encoding 相对应,都是处理字符编码,一旦处理不好,就会导致中文乱码问题。
Python2 的字符编码乱七八糟,也广被人诟病,如果不幸遇到 Python2 项目,可参照如下例子解决。
字符串在 Python2 内部的表示是 unicode 编码。因此,在做编码转换时,需要以 unicode 作为中间编码,即先将其他编码的字符串解码(decode)成 unicode,再从 unicode 编码(encode)成另一种编码。
# -*- coding: utf-8 -*-
m = {'a' : '你好'}

print m
=>{'a': '\xe4\xbd\xa0\xe5\xa5\xbd'}

print json.dumps(m)
=>{"a": "\u4f60\u597d"}

print json.dumps(m,ensure_ascii=False)
=>{"a": "浣犲ソ"}

print json.dumps(m,ensure_ascii=False).decode('utf8').encode('gb2312')
=>{"a": "你好"}

Python3 的默认编码格式是 utf-8,以上例子,只需要ensure_ascii=False,就能解决。