json格式数据作为如今越来越流行的数据交换格式,几乎已经成为web端数据交互的标准,主流的数据科学语言R,Python都中都有非常完善的半结构化数据与json数据进行通讯。本篇文章将会通过简单案例介绍R语言与Python中与json数据进行序列化与反序列化的常用函数。
json的数据以键值对形式存在,在R语言中,符合此标准的就是基础数据对象中的list(严格来说,R语言中所有数据对象都可以表示为list,但是可以保存递归结构只有list一种)。
在R语言中,涉及到json数据处理的,主要是list转换为json和json转换为普通的list。前者被称为序列化,后者被称为反序列化。(也可以理解为编码或者解码的过程)虽然R语言中有三个包可以处理json序列化与反序列化过程(rjson、RJSONIO、jsonlite),但是实际应用最多,功能相对完善的,要数最后一个jsonlite包,这里仅以jsonlite包为例进行讲解。
library("jsonlite")
library("magrittr")
library("RCurl")
mylist
"name"="Raiders of the Lost Ark",
"year"=1981,
"actors"=list(
"Indiana Jones" = "Harrison Ford",
"Dr. Ren Belloq"= "Paul Freeman"
),
"producers"= c("Frank Marshall", "George Lucas", "Howard Kazanjian"),
"budget" = 18000000,
"academy_award_ve"= TRUE
)
jsonlite包中的toJSON函数负责将R语言中的数据对象(主要是list)进行序列化。序列化之前需要声明一点,llist必须提供命名,因为json需要严格的键值对结构。
toJSON函数有两个需要强调的参数。
第一个是auto_unbox参数,这个参数控制json对象中值(value)在长度为1时,是否强制转换为数组。如果value对象长度唯一,通常不需要数组化,(因为R语言中没有标量,长度为一的字符或者数值都是原子型向量,默认也会被转换为数组【长度为1】)在大多数场合下,需要指定参数auto_unbox为TRUE。
auto_unbox参数默认为FALSE时:
toJSON(mylist)
{"name":["Raiders of the Lost Ark"],"year":[1981],"actors":{"Indiana Jones":["Harrison Ford"],"Dr. Ren Belloq":["Paul Freeman"]},"producers":["Frank Marshall","George Lucas","Howard Kazanjian"],"budget":[18000000],"academy_award_ve":[true]}
auto_unbox参数指定为TRUE时:
toJSON(mylist,auto_unbox = TRUE)
{"name":"Raiders of the Lost Ark","year":1981,"actors":{"Indiana Jones":"Harrison Ford","Dr. Ren Belloq":"Paul Freeman"},"producers":["Frank Marshall","George Lucas","Howard Kazanjian"],"budget":18000000,"academy_award_ve":true}
prettty参数用于控制输出格式是否进行排版美化(换行渲染)
toJSON(mylist,auto_unbox = TRUE,pretty = TRUE)
{
"name" : "Raiders of the Lost Ark",
"year" : 1981,
"actors" : {
"Indiana Jones": "Harrison Ford",
"Dr. Ren Belloq": "Paul Freeman"
},
"producers": ["Frank Marshall", "George Lucas", "Howard Kazanjian"],
"budget" : 18000000,
"academy_award_ve": true
}
但是pretty参数仅仅是一个美化参数,仅仅为了更好地进行可视化输出,对于json内容并没有任何影响,在使用时(特别是WEB上传json参数时,通常没有必要设置)。
反序列化:
这里的反序列化就是指如何将一组json字符串反序列化为R语言中的list结构,这种需求在网络数据抓取中使用的及其频繁。
第一种情况:当json字符串来自于手动创建的字符串时:
myjson
fromJSON(myjson)
$name
[1] "Raiders of the Lost Ark"
$year
[1] 1981
$actors
$actors$`Indiana Jones`
[1] "Harrison Ford"
$actors$`Dr. Ren Belloq`
[1] "Paul Freeman"
$producers
[1] "Frank Marshall" "George Lucas" "Howard Kazanjian"
$budget
[1] 18000000
$academy_award_ve
[1] TRUE
因为json字符串中规定使用英文双引号来包裹所有key键名和字符串格式的value值,所有自己手动建立的包含有json字符串向量时,要使用英文单引号进行表示。这样不至于引起R语言中符号逻辑的混乱。
如果非要使用双引号来建立时,则必须在json字符串内部的所有双引号前使用“\”进行转义,否则R语言无法识别。
myjson
fromJSON(myjson)
$name
[1] "Raiders of the Lost Ark"
$year
[1] 1981
$actors$actors
$`Indiana Jones`
[1] "Harrison Ford"
$actors$`Dr. Ren Belloq`
[1] "Paul Freeman"
$producers
[1] "Frank Marshall" "George Lucas" "Howard Kazanjian"
$budget
[1] 18000000
$academy_award_ve
[1] TRUE
如果看过之前的几篇关于web抓取的文章,你已经好奇为啥web返回的json原始字符串向量里面存在大量的“\”和“\r\n”。
url
getURL(url)
"[\r\n {\r\n \"name\":\"van Pelt, Lucy\",\r\n \"sex\":\"female\",\r\n \"age\":32\r\n },\r\n {\r\n \"name\":\"Peppermint, Patty\",\r\n \"sex\":\"female\",\r\n \"age\":null\r\n },\r\n {\r\n \"name\":\"Brown, Charlie\",\r\n \"sex\":\"male\",\r\n \"age\":27\r\n }\r\n]"
getURL(url) %>% cat()
[
{ "name":"van Pelt, Lucy", "sex":"female", "age":32
},
{ "name":"Peppermint, Patty", "sex":"female", "age":null
},
{ "name":"Brown, Charlie", "sex":"male", "age":27
}
]
实际上这里很好解释,从web端返回的json数据内部所有的分隔符都是双引号,而反会的整个json字串整体作为一个长度为1的原子型字符串向量,但是在R语言中,字符串向量默认使用双引号进行分割,这样就导致json内层的双引号与外侧字符串向量的分割符出现冲突,如果不做任何更改,这样的格式是R语言无法识别的。
fromJSON("{"name":"duyu"}")
Error: unexpected symbol in "fromJSON("{"name"
fromJSON("{\"name\":\"duyu\"}")
$name
[1] "duyu"
所以R语言自动给内层的json分隔符【也就是内层的所有双引号全部都加了转义符,至于”\r\n”,那仅仅是一个换行符,用于优化json排版,使用cat函数可以渲染出最终的效果】。这才是在R语言中,json返回值中出现大量反斜杠的原因。
Python:
Python中主要使用json包进行json的序列化与反序列化。
json序列化:
import json
mydict = {
"name":"Raiders of the Lost Ark",
"year":1981,
"actors":{
"Indiana Jones" :"Harrison Ford",
"Dr. Ren Belloq": "Paul Freeman"
},
"producers": ["Frank Marshall", "George Lucas", "Howard Kazanjian"],
"budget" : 18000000,
"academy_award_ve": True
}
json.dumps(mydict)
'{"name": "Raiders of the Lost Ark", "year": 1981, "actors": {"Indiana Jones": "Harrison Ford", "Dr. Ren Belloq": "Paul Freeman"}, "producers": ["Frank Marshall", "George Lucas", "Howard Kazanjian"], "budget": 18000000, "academy_award_ve": true}'
序列化的场景主要用在web请求,如果要求参数提交以json格式提交的话,就需要序列化之后进行提交。(仔细观察你会发现json的数据格式与Python中的dict出奇的一致,确实挺像,但是很多细节明显不一样,比如布尔值,py中是True,json中是true)
反序列化同样涉及到自建json字符串。与R语言中情形一样,使用英文单引号作为字符串分隔符,内层的json字符串对象则必须使用双引号作为分割符号。这样不会导致内外层符号混乱。
myjson = '{"name": "Raiders of the Lost Ark", "year": 1981, "actors": {"Indiana Jones": "Harrison Ford", "Dr. Ren Belloq": "Paul Freeman"}, "producers": ["Frank Marshall", "George Lucas", "Howard Kazanjian"], "budget": 18000000, "academy_award_ve": true}'
json.loads(myjson)
{'academy_award_ve': True, 'actors': {'Dr. Ren Belloq': 'Paul Freeman', 'Indiana Jones': 'Harrison Ford'}, 'budget': 18000000, 'name': 'Raiders of the Lost Ark', 'producers': ['Frank Marshall', 'George Lucas', 'Howard Kazanjian'], 'year': 1981}
但是你如果再建立字符串时,坚持使用双引号作为分隔符,为了防止符号混乱,同样需要使用右斜杠转义。
myjson = "{\"name\": \"Raiders of the Lost Ark\", \"year\": 1981, \"actors\": {\"Indiana Jones\": \"Harrison Ford\", \"Dr. Ren Belloq\": \"Paul Freeman\"}, \"producers\": [\"Frank Marshall\", \"George Lucas\", \"Howard Kazanjian\"], \"budget\": 18000000, \"academy_award_ve\": true}"
json.loads(myjson)
{'academy_award_ve': True, 'actors': {'Dr. Ren Belloq': 'Paul Freeman', 'Indiana Jones': 'Harrison Ford'}, 'budget': 18000000, 'name': 'Raiders of the Lost Ark', 'producers': ['Frank Marshall', 'George Lucas', 'Howard Kazanjian'], 'year': 1981}
json数据通常来源于webd端的数据请求返回值,但是在Python中,返回值的原始向量,并不会出现像R语言中那种里面存在大量反斜杠的情况,原因在于,Python的字符串分割符默认使用英文单引号(R语言中默认使用英文双引号)。而web端返回的json值严格规定使用英文双引号作为分隔符,这样内层是双引号,外层默认是单引号,所以不会引起歧义,不需要使用反斜杠进行转义。
import requests
url = "http://www.r-datacollection.com/materials/ch-3-xml/peanuts.json"requests.get(url).text'[\r\n {\r\n "name":"van Pelt, Lucy",\r\n "sex":"female",\r\n "age":32\r\n },\r\n {\r\n "name":"Peppermint, Patty",\r\n "sex":"female",\r\n "age":null\r\n },\r\n {\r\n "name":"Brown, Charlie",\r\n "sex":"male",\r\n "age":27\r\n }\r\n]'
可以看到返回值仅仅是包含有\r\n这种换行符(用于美化json排版)。
好在requests函数有一个默认的json方法用于直接处理json返回值。
requests.get(url).json()
[{'age': 32, 'name': 'van Pelt, Lucy', 'sex': 'female'}, {'age': None, 'name': 'Peppermint, Patty', 'sex': 'female'}, {'age': 27, 'name': 'Brown, Charlie', 'sex': 'male'}]
以上json方法调用直接回直接将json字符串转换为Python中的内建对象,dict,但是如果使用urllib包请求,可能就需要使用json库中的json.loads()函数进行反序列化了。
json.loads(requests.get(url).text)
[{'age': 32, 'name': 'van Pelt, Lucy', 'sex': 'female'}, {'age': None, 'name': 'Peppermint, Patty', 'sex': 'female'}, {'age': 27, 'name': 'Brown, Charlie', 'sex': 'male'}]
以上便是R语言与Python中内建非结构化数据对象(list、dict)与json之间的转化方法与核心要点及注意事项。这些函数一定要牢记,在遇到非结构化数据处理时经常用到。
在线课程请点击文末原文链接: