最近用python开发公司网络协议的解析工具,直接从网络抓包pcap文件中解析出协议交换流程.公司协议中二进制和Json两种格式类型共存.针对二进制内容,python的struct这库用起来还是蛮顺手的.针对Json协议部分,首选就是本文的Json库.对于Json库的详细介绍,可以网上搜一把,资料还是很全的.这里讨论的是json库的loads方法.
由于struct库获取过来的数据,都是以pythonh中的字符串类型的保存的,所以我采用json的loads方法(load是从文件中加载).于是乎, 问题就出现了, 我使用时loads抛出了一个ValueError异常.
查看下程序堆栈信息,可以发现loads方法的调用流程是:
loads()->_default_decoder.decode(s)
其中decode的库源代码如下:
就是匹配空白字符的一个正则表达式对象,所以
_w这老兄又把end加上了json字符串后面空白字符串的长度(如果有的话).
由于struct库获取过来的数据,都是以pythonh中的字符串类型的保存的,所以我采用json的loads方法(load是从文件中加载).于是乎, 问题就出现了, 我使用时loads抛出了一个ValueError异常.
查看下程序堆栈信息,可以发现loads方法的调用流程是:
loads()->_default_decoder.decode(s)
其中decode的库源代码如下:
def decode(self, s, _w=WHITESPACE.match):
"""Return the Python representation of ``s`` (a ``str`` or ``unicode``
instance containing a JSON document)
"""
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
end = _w(s, end).end()
if end != len(s):
raise ValueError(errmsg("Extra data", s, end, len(s)))
return obj
其中WHITESPACE是一个正则表示式对象,其定义代码如下:
WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
就是匹配空白字符的一个正则表达式对象,所以
idx=_w(s, 0).end()
就是把字符串前面的空白字符都跳过了soga.PS:正则表示式对象的match方法的第二个入参,代表匹配的起始位置
然后self.raw_decode完成了字符串内容的解析(相关代码可以自己研究哦),返回了一个obj对象(这货就是我们需要的)和一个end( json格式中‘}’的位置+1,end返回一个开区间的结束位置) ,end现在代表json处理了的字符串的长度
end = _w(s, end).end()
_w这老兄又把end加上了json字符串后面空白字符串的长度(如果有的话).
主题来了:
if end != len(s):
raise ValueError(errmsg("Extra data", s, end, len(s)))
如果json处理的字符串长度和我们给的长度不一致,那就game over,抛出一个ValueError异常了.什么情况下会导致长度不统一呢,根据json库的算法,只要你在后面的空白字符再跟上其他非空白字符,那就能得到梦寐以求的ValueError.由于我要解析的json字符串,后面都会带上c字符结尾符0,所以这就是真相.
我的解决方法是,对到手的json字符串先预处理下:
strJson = "".join([ string.strip().rsplit("}" , 1)[0] , "}"] )
主要就是用rsplit函数切掉最右边“}”后面的字符串.PS:我是感觉end!=len(s)这个判断不是很好,不过stackoverflow上面一些家伙的解释,是json库不支持解释"{}{}"这种类型的json字符串,从这方法考虑,倒也说的通.
PS2:为什么不支持?有时间,可以自己实现支持{}{}格式的解析
PS3:最后贴上我工具中json串格式转换部分:
def FormatJsonString(string , keySpace = 4):
#处理json对象
def StrJsonObj(objJson , deep ):
lstJson= []
<span style="white-space:pre"> </span>#return "".join([<span style="font-family: Arial, Helvetica, sans-serif;">StrJsonNote( item , deep) for item in objJson.items() ] )</span>
for item in objJson.items() :
lstJson.append(StrJsonNote( item , deep) )
return "".join(lstJson)
#处理json节点
def StrJsonNote(objNote , deep):
key , content = objNote
retStr = "%s%-*s : " % (" " * deep , keySpace , key)
return "".join([retStr , StrJsonContent(content , deep)])
#处理json节点内容
def StrJsonContent(content , deep , strTab="" , inList = False):
retStr = ""
if isinstance( content , dict):
if inList == False:
retStr = "".join(["\n" , StrJsonObj( content , deep+1 )])
else:
retStr = StrJsonObj( content , deep+1 )
elif isinstance( content , list):
lstResult = ["\n"]
nextDepp = deep+1
count = 0
for item in content:
lstResult.append(StrJsonContent(item , nextDepp , " " * nextDepp , True) )
count += 1
retStr = "".join(lstResult)
elif isinstance( content , unicode):
retStr = "".join( [('%s"%s"' % ( strTab , content) ) , "\n"] )
else:
retStr = "".join( [("%s%s" % (strTab , content)) , "\n"] )
return retStr
strJson = "".join([ string.strip().rsplit("}" , 1)[0] , "}"] ) #处理外部的原始json字符串
objJson = json.loads(strJson) #转换成json字典
return StrJsonObj(objJson , 0)
版权声明:本文为vxwumin原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。