如何在Python中从套接字读取JSON?(增量解析JSON)
我打开了一个套接字,想从中读取一些json数据。问题是,标准库里的json
模块只能从字符串中解析数据(load
方法只能读取整个文件,然后在里面调用loads
)。看起来这个模块的所有功能都依赖于参数是字符串。
这在使用套接字时是个大问题,因为你无法一次性把所有数据都读成字符串,而且你也不知道在解析之前需要读取多少字节。
所以我想问:有没有什么简单优雅的解决办法?有没有其他的json库可以逐步解析数据?自己写一个这样的库值得吗?
补充一下:这是XBMC的jsonrpc API。消息没有封装格式,我对格式没有控制。每条消息可能在一行上,也可能分成几行。我可以写一个简单的解析器,只需要某种形式的getc函数,然后用s.recv(1)
来喂它,但这样做似乎不太符合Python的风格,而且我有点懒得去做这个:-)
7 个回答
如果你是通过HTTP流获取JSON数据的,可以使用Content-Length
这个头信息来获取JSON数据的长度。例如:
import httplib
import json
h = httplib.HTTPConnection('graph.facebook.com')
h.request('GET', '/19292868552')
response = h.getresponse()
content_length = int(response.getheader('Content-Length','0'))
# Read data until we've read Content-Length bytes or the socket is closed
data = ''
while len(data) < content_length or content_length == 0:
s = response.read(content_length - len(data))
if not s:
break
data += s
# We now have the full data -- decode it
j = json.loads(data)
print j
编辑:考虑到你没有定义协议,这个内容可能不太有用,但在其他情况下可能会有帮助。
假设这是一个流式(TCP)套接字,你需要自己实现消息的封装机制(或者使用一个已经存在的更高级的协议)。一种简单的方法是把每条消息的长度定义为一个32位的整数,然后再跟着那么多字节的数据。
发送方:先计算出JSON数据包的长度,把它用struct
模块打包成4个字节,然后通过套接字发送这个长度,再发送JSON数据包。
接收方:不断从套接字读取数据,直到至少有4个字节,使用struct.unpack
来解包这个长度。然后继续从套接字读取数据,直到你有了至少这么多字节的数据,这就是你的JSON数据包;剩下的部分就是下一个消息的长度。
如果你将来想通过同一个套接字发送除了JSON以外的其他类型的消息,你可能需要在长度和数据之间发送一个消息类型代码;恭喜你,这样你就发明了另一个协议。
还有一种稍微标准一点的方法是DJB的Netstrings协议;它和上面提到的系统非常相似,但使用的是文本编码的长度,而不是二进制;这个协议被像Twisted这样的框架直接支持。
你想要的是ijson,这是一个增量的JSON解析器。你可以在这里找到它:https://pypi.python.org/pypi/ijson/。使用起来应该很简单(直接从那个页面复制的):
import ijson.backends.python as ijson
for item in ijson.items(file_obj):
# ...
(对于那些喜欢自给自足的东西的人——也就是说只依赖标准库:我昨天写了一个小的包装器,围绕json做的——只是因为我不知道ijson。可能效率要低很多。)
编辑:因为我发现其实(一个经过Cython优化的版本)我的方法比ijson高效得多,所以我把它打包成了一个独立的库——这里也有一些粗略的基准测试结果:http://pietrobattiston.it/jsaone