我打开了一个套接字,我想从中读取一些json数据。问题是标准库中的json
模块只能从字符串中解析(load
只读取整个文件并在其中调用loads
)它甚至在模块内部一直看起来一切都取决于参数是字符串。
这是套接字的真正问题,因为你永远无法将它全部读取为字符串,并且在实际解析它之前你不知道要读取多少字节。
所以我的问题是:是否有(简单而优雅)的解决方法?是否有另一个可以递增地解析数据的json库?值得自己写一下吗?
编辑:这是XBMC jsonrpc api。没有消息信封,我无法控制格式。每条消息可以在一行或多行上。
我可以编写一些简单的解析器,它只需要某种形式的getc函数并使用s.recv(1)
来提供它,但这不是一个非常pythonic的解决方案而且我有点懒,这样做: - )
答案 0 :(得分:4)
编辑:鉴于您没有定义协议,这没有用,但它可能在其他环境中很有用。
假设它是一个流(TCP)套接字,您需要实现自己的消息成帧机制(或使用现有的更高级别协议)。一种直接的方法是将每条消息定义为32位整数长度字段,然后是那么多字节的数据。
Sender:获取JSON数据包的长度,用struct
模块将其打包成4个字节,在套接字上发送,然后发送JSON数据包。
Receiver:重复读取套接字,直到您有至少4个字节的数据,使用struct.unpack
解压缩长度。从套接字读取,直到你拥有至少那么多的数据,这是你的JSON数据包;遗留下来的是下一条消息的长度。
如果在某个时候您想要在同一个套接字上发送由JSON以外的内容组成的消息,您可能希望在长度和数据有效负载之间发送消息类型代码;恭喜你,你已经发明了另一种协议。
另一种稍微更标准的方法是DJB的Netstrings协议;它与上面提出的系统非常相似,但是使用文本编码的长度而不是二进制;它由Twisted等框架直接支持。
答案 1 :(得分:3)
如果您从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
答案 2 :(得分:3)
你想要的(ed)是ijson,一个增量的json解析器。 它可以在这里找到:https://pypi.python.org/pypi/ijson/。用法应该很简单(从该页面复制):
import ijson.backends.python as ijson
for item in ijson.items(file_obj):
# ...
(对于那些喜欢自足的人 - 从某种意义上说它只依赖于标准库:我昨天写了一个围绕json的小包装 - 但仅仅因为我不知道ijson。它可能更少高效。)
编辑:因为我发现事实上(一个cythonized版本)我的方法比ijson更有效,我把它打包成一个独立的库 - 在这里也看一些粗略的基准:http://pietrobattiston.it/jsaone
答案 3 :(得分:2)
你有控制json吗?尝试将每个对象写为一行。然后在套接字上执行readline调用described here。
infile = sock.makefile()
while True:
line = infile.readline()
if not line: break
# ...
result = json.loads(line)
答案 4 :(得分:0)
略读XBMC JSON RPC文档,我想你想要一个现有的JSON-RPC库 - 你可以看看: http://www.freenet.org.nz/dojo/pyjson/
如果这不适合任何原因,我认为每个请求和响应都包含在一个JSON对象中(而不是一个松散的JSON原语,可能是一个字符串,数组或数字),所以信封你'重新寻找的是定义JSON对象的'{...}'。
因此,我会尝试类似(伪代码):
while not dead:
read from the socket and append it to a string buffer
set a depth counter to zero
walk each character in the string buffer:
if you encounter a '{':
increment depth
if you encounter a '}':
decrement depth
if depth is zero:
remove what you have read so far from the buffer
pass that to json.loads()
答案 5 :(得分:0)
您可能会发现JSON-RPC对这种情况很有用。它是一个远程过程调用协议,应该允许您调用XBMC JSON-RPC公开的方法。您可以在Trac上找到规范。
答案 6 :(得分:0)
res = str(s.recv(4096), 'utf-8') # Getting a response as string
res_lines = res.splitlines() # Split the string to an array
last_line = res_lines[-1] # Normally, the last one is the json data
pair = json.loads(last_line)
https://github.com/A1vinSmith/arbitrary-python/blob/master/sockets/loopHost.py