我心中的王者:Python-第24章 JSON资料
JSON是一种数据格式,由美国程序设计师Douglas Crockford创建的,JSON全名是JavaScript Object Notation,由JSON英文全文字义我们可以推敲JSON的缘由,最初是为JavaScript开发的。这种数据格式由于简单好用被大量应用在Web开发与大数据数据库(NoSQL),现在已成为一种著名数据格式,Python与许多程序语言同时采用与支持。也由此在使用Python设计程序时,可以将数据以JSON格式存储,与其他程序语言的设计师分享。
Python程序设计时需使用import json导入json模块。
24-1 认识json数据格式
json的数据格式有2种,分别是:
对象(object):一般用大括号{ }表示。
数组(array):一般用中括号[ ]表示。
24-1-1 对象(object)
在json中对象就是用“键-值(key:value)”方式配对存储,对象内容用左大括号“{”开始,右大括号“}”结束,键(key)和值(value)用“:”区隔,每一组键:值间以逗号“,”隔开,以下是取材自json.org的官方图说明。
在json格式中键(key)是一个字符串(string)。值可以是数值(number)、字符串(string)、布尔值(bool)、数组(array)或是null值。
例如:下列是对象的实例。
{"Name":"Hung","Age":25}
使用json时需留意,键(key)必须是文字,例如下列是错误的实例。
{"Name":"Hung", 25:"Key"}
在json格式中字符串需用双引号,同时在json文件内不可以有批注。
24-1-2 数组(array)
数组基本上是一系列的值(value)所组成,用左中括号“[”开始,右中括号“]”结束。各值之间用逗号“,”隔开,以下是取材自json.org的官方图说明。
数组的值可以是数值(number)、字符串(string)、布尔值(bool)、数组(array)或是null值。
24-1-3 json数据存在方式
前两节所述是json的数据格式定义,但是在Python中它存在方式是字符串(string)。
‘json数据' # 可参考程序实例ch24_1.py的第3个输出
使用json模块执行将Python数据转成json字符串类型数据或json文件使用不同方法,下面24-2和24-3节将分别说明。
24-2 将Python应用在json字符串形式数据
本节主要说明json数据以字符串形式存在时的应用。
24-2-1 使用dumps( )将Python数据转成json格式
在json模块内有dumps( ),可以将Python数据转成json字符串格式,下列是转化对照表。
程序实例ch24_1.py:将Python的列表与元组数据转成json的数组数据的实例。
# ch24_1.py
import json
listNumbers = [5, 10, 20, 1] # 列表数据
tupleNumbers = (1, 5, 10, 9) # 元组资料
jsonData1 = json.dumps(listNumbers) # 将列表数据转成json数据
jsonData2 = json.dumps(tupleNumbers) # 将列表数据转成json数据
print("列表转换成json的数组", jsonData1)
print("元组转换成json的数组", jsonData2)
print("json数组在Python的数据类型 ", type(jsonData1))
执行结果
列表转换成json的数组 [5, 10, 20, 1]
元组转换成json的数组 [1, 5, 10, 9]
json数组在Python的数据类型 <class 'str'>
特别留意,上述笔者在第10行打印最终json在Python的数据类型,结果是以字符串方式存在。若以jsonData1为例,从上述执行结果我们可以了解,在Python内它的数据如下:
‘[5, 10, 20, 1]'
程序实例ch24_2.py:将Python由字典元素所组成的列表转成json数组,转换后原先字典元素变为json的对象。
# ch24_2.py
import json
listObj = [{'Name':'Peter', 'Age':25, 'Gender':'M'}] # 列表数据元素是字典
jsonData = json.dumps(listObj) # 将列表数据转成json数据
print("列表转换成json的数组", jsonData)
print("json数组在Python的数据类型 ", type(jsonData))
执行结果
列表转换成json的数组 [{"Name": "Peter", "Age": 25, "Gender": "M"}]
json数组在Python的数据类型 <class 'str'>
读者应留意json对象的字符串是用双引号。
24-2-2 dumps( )的sort keys参数
Python的字典是无序的数据,使用dumps( )将Python数据转成json对象时,可以增加使用sort_keys=True,则可以将转成json格式的对象排序。
程序实例ch24_3.py:将字典转成json格式的对象,分别是未使用排序与使用排序。最后将未使用排序与使用排序的对象作比较判断是否相同,得到的是被视为不同对象。
# ch24_3.py
import json
dictObj = {'b':80, 'a':25, 'c':60} # 字典
jsonObj1 = json.dumps(dictObj) # 为排序将字典转成json对象
jsonObj2 = json.dumps(dictObj, sort_keys=True) # 有排序将字典转成json对象
print("未用排序将字典转换成json的对象", jsonObj1)
print("使用排序将字典转换成json的对象", jsonObj2)
print("有排序与未排序对象是否相同 ", jsonObj1 == jsonObj2 )
print("json物件在Python的数据类型 ", type(jsonObj1))
执行结果
未用排序将字典转换成json的对象 {"b": 80, "a": 25, "c": 60}
使用排序将字典转换成json的对象 {"a": 25, "b": 80, "c": 60}
有排序与未排序对象是否相同 False
json物件在Python的数据类型 <class 'str'>
从上述执行结果得出json对象在Python的存放方式也是字符串。
24-2-3 dumps( )的indent参数
从ch24_3.py的执行结果可以看到数据不太容易阅读,特别是资料量如果更多的时候,在将Python的字典数据转成json格式的对象时,可以加上indent设定缩排json对象的键-值,让json对象可以更容易显示。
程序实例ch24_4.py:将Python的字典转成json格式对象时,设定缩排4个字符宽度。
# ch24_4.py
import json
dictObj = {'b':80, 'a':25, 'c':60} # 字典
jsonObj = json.dumps(dictObj, sort_keys=True, indent=4) # 用内缩呈现json对象
print(jsonObj)
执行结果
{
"a": 25,
"b": 80,
"c": 60
}
24-2-4 使用loads( )将json格式数据转成Python的数据
在json模块内有loads( ),可以将json格式数据转成Python数据,下列是转化对照表。
程序实例ch24_5.py:将json的对象数据转成Python数据的实例,需留意在建立json数据时,需加上引号,因为json数据在Python内是以字符串形式存在。
# ch24_5.py
import json
jsonObj = '{"b":80, "a":25, "c":60}' # json物件
dictObj = json.loads(jsonObj) # 转成Python物件
print(dictObj)
print(type(dictObj))
执行结果
{'b': 80, 'a': 25, 'c': 60}
<class 'dict'>
从上述可以看到json对象返回Python数据时的数据类型。
24-3 将Python应用在json文件
我们在程序设计时,更重要的是将Python的资料以json格式存储,未来可以供其他不同的程序语言读取。或是使用Python读取其他语言以json格式存储的数据。
24-3-1 使用dump( )将Python数据转成json文件
在json模块内有dump( ),可以将Python数据转成json文件格式,这个文件格式的扩展名是json,下列将直接以程序实例解说dump( )的用法。
程序实例ch24_6.py:将一个字典数据,使用json格式存储在out24_6.json文件内。在这个程序实例中,dump( )方法的第一个参数是欲存储成json格式的数据,第二个参数是欲存储的文件对象。
# ch24_6.py
import json
dictObj = {'b':80, 'a':25, 'c':60}
fn = 'out24_6.json'
with open(fn, 'w') as fnObj:
json.dump(dictObj, fnObj)
执行结果 在目前工作文件夹可以新增json文件,文件名是out24_6.json。如果用记事本打开,可以得到下列结果。
24-3-2 使用load( )读取json文件
在json模块内有load( ),可以读取json文件,读完后这个json文件将被转换成Python的数据格式,下列将直接以程序实例解说dump( )的用法。
程序实例ch24_7.py:读取json文件out24_6.json,同时列出结果。
# ch24_7.py
import json
fn = 'out24_6.json'
with open(fn, 'r') as fnObj:
data = json.load(fnObj)
print(data)
print(type(data))
执行结果
{'b': 80, 'a': 25, 'c': 60}
<class 'dict'>
24-4 简单的json文件应用
程序实例ch24_8.py:程序执行时会要求输入账号,然后列出所输入账号同时打印欢迎使用本系统。
# ch24_8.py
import json
fn = 'login.json'
login = input("请输入账号 : ")
with open(fn, 'w') as fnObj:
json.dump(login, fnObj)
print("%s! 欢迎使用本系统! " % login)
执行结果
请输入账号 : 晓波
晓波! 欢迎使用本系统!
上述程序同时会将所输入的账号存入login.json文件内。
程序实例ch24_9.py:读取login.json的数据,同时输出“欢迎回来使用本系统”。
# ch24_9.py
import json
fn = 'login.json'
with open(fn, 'r') as fnObj:
login = json.load(fnObj)
print("%s! 欢迎回来使用本系统! " % login)
执行结果
晓波! 欢迎回来使用本系统!
程序实例ch24_10.py:下列程序基本上是ch24_8.py和ch24_9.py的组合,如果第一次登录会要求输入账号然后将输入账号记录在login24_10.json文件内,如果不是第一次登录,会直接读取已经存在login24_10.json的账号,然后打印“欢迎回来”。这个程序用第7行是否能正常读取login24_10.json方式判断是否是第一次登录,如果这个文件不存在表示是第一次登录,将执行第8行except至12行的内容。如果这个文件已经存在,表示不是第一次登录,将执行第13行else:后面的内容。
# ch24_10.py
import json
fn = 'login24_10.json'
try:
with open(fn) as fnObj:
login = json.load(fnObj)
except Exception:
login = input("请输入账号 : ")
with open(fn, 'w') as fnObj:
json.dump(login, fnObj)
print("系统已经记录你的账号 ")
else:
print("%s 欢迎回来" % login)
执行结果
andyyah 欢迎回来
24-5 世界人口数据的json文件
在本书ch24文件夹内有populations.json文件,这是一个非官方在2000年和2010年的人口统计数据,这一节笔者将一步一步讲解如何使用json数据文件。
24-5-1 认识人口统计的json文件
若是将这个文件用记事本打开,内容如下:
在网络上任何一个号称是真实统计的json数据,在用记事本打开后,初看一定是复杂的,读者碰上这个问题首先不要慌,同时分析数据的共通性,这样有助于未来程序的规划与设计。从上图基本上我们可以了解它的资料格式,这是一个列表,列表元素是字典,有些国家或地区只有2000年的资料,有些只有2010年的资料,有些则同时有这2个年度的数据,每个字典内有4个键-值,如下所示:
上述字段分别是国家或地区名称(Country Name)、国家代码(Country Code)、年份(Year)和人口数(numbers)。从上述文件我们应该注意到,人口数在我们日常生活理解应该是整数,可是这个数据是用字符串表达,另外,在非官方的统计数据中,难免会有错误,例如,上述国家或地区(这是全球人口统计)的2010年人口数据资料出现了小数点,这个需要我们用程序处理。
程序实例ch24_11.py:列出populations.json数据中的人口数据。
# ch24_11.py
import json
fn = 'populations.json'
with open(fn) as fnObj:
getDatas = json.load(fnObj) # 读json档案
for getData in getDatas:
if getData['Year'] == '2000': # 筛选2000年的数据
countryName = getData['Country Name']
countryCode = getData['Country Code']
population = int(float(getData['Numbers']))
print('代码 =', countryCode,
'名称 =', countryName,
'人口数 =', population)
执行结果
代码 = WLD 名称 = World 人口数 = 6117806174
代码 = AFG 名称 = Afghanistan 人口数 = 25951672
代码 = ALB 名称 = Albania 人口数 = 3072478
代码 = DZA 名称 = Algeria 人口数 = 30534041
代码 = ASM 名称 = American Samoa 人口数 = 57995
代码 = AND 名称 = Andorra 人口数 = 65258
代码 = AGO 名称 = Angola 人口数 = 13926705
代码 = ATG 名称 = Antigua and Barbuda 人口数 = 78536
代码 = ARG 名称 = Argentina 人口数 = 36931013
代码 = ARM 名称 = Armenia 人口数 = 3076653
代码 = ABW 名称 = Aruba 人口数 = 91031
上述重点是第12行,当我们碰上含有小数点的字符串时,需先将这个字符串转成浮点数,然后再将浮点数转成整数。
24-5-2 认识pygal.maps.world的地区代码信息
前一节有关populations.json地区代码是3个英文字母,如果我们想要使用这个json数据绘制世界人口地图,需要配合pygal.maps.world模块的方法,这个模块的地区代码是2个英文字母,所以需要将populations.json地区代码转成2个英文字母。这里本节先介绍2个英文字母的信息,pygal.maps.world模块内有COUNTRIES字典,在这个字典中国码是2个英文字母,从这里我们可以列出相关地区与代码的列表。使用pygal.maps.world模块前需先安装此模块,如下所示:
pip install pygal.maps.world
程序实例ch24_12.py:列出pygal.maps.world模块COUNTRIES字典的2个英文字母的地区代码与完整的地区名称列表。
# ch24_12.py
from pygal.maps.world import COUNTRIES
for countryCode in sorted(COUNTRIES.keys()):
print("代码 :", countryCode, " 名称 = ", COUNTRIES[countryCode])
执行结果
接着笔者将讲解,输出2个字母的地区代码时,同时输出,这个程序相当于是将2个不同来源的数据作配对。
程序实例ch24_13.py:从populations.json取每个名称信息,然后将每一个名称放入getCountryCode( )方法中找寻相关代码,如果找到则输出相对应的代码,如果找不到则输出“名称不吻合”。
# ch24_13.py
import json
from pygal.maps.world import COUNTRIES
def getCountryCode(countryName):
'''输入名称回传代码'''
for dictCode, dictName in COUNTRIES.items(): # 搜寻代码字典
if dictName == countryName:
return dictCode # 如果找到则回传代码
return None # 找不到则回传None
fn = 'populations.json'
with open(fn) as fnObj:
getDatas = json.load(fnObj) # 读取人口数据json档案
for getData in getDatas:
if getData['Year'] == '2000': # 筛选2000年的数据
countryName = getData['Country Name']
countryCode = getCountryCode(countryName)
population = int(float(getData['Numbers']))
if countryCode != None:
print(countryCode, ":", population) # 名称相符
else:
print(countryName," 名称不吻合:") # 名称不吻合
执行结果
上述会有不吻合输出是因为这是2个不同单位的数据,例如,Arab World在populations.json是一个记录,在pygal.maps.world模块的COUNTRIES字典中没有这个记录。至于有关上述的更深层应用,将在下一节解说。