问题

我们再次回到jsonpath的问题,起初使用jsonpath-ng实现快速访问json对象节点。但jsonpath-ng的性能问题,导致这个方法实在是糟糕,无法使用,悲剧的是已经写了很多代码,再用全路径访问json工作量有点太大,有点进退两难。

回过头思考,起初我的目的,就是不必要写全路径访问json节点,通过jsonpath只需要写一个节点进行模糊匹配,实现节点访问。实际上该需求并不复杂,并没有用到jsonpath的复杂功能。能不能不用jsonpath,也能实现?

幸运的是方案是存在的,而且实现起来也很容易。python语言天生就支持json对象,不需要再进行对象的序列化,可以直接利用这个语言特性。

方案

节点对象获取

首先我们解决json节点获取问题。我们写下如下的代码:

def get_one_json_node(data: dict, field: str):

if isinstance(data, list):

for it in data:

ret = get_one_json_node(it, field)

if ret is not None:

return ret

if not isinstance(data, dict):

return None

if field in data.keys():

return data[field]

for it in data.keys():

ret = get_one_json_node(data[it], field)

if ret:

return ret

return None

测试代码:

class TestJsonGet:

js = {'a': 10, 'b': 20, 'c': {'e': 10, 'f': 'string'}, 'c1': {'e': {'f1': 30}, 'f': 'string'},

'c2': {'e': 10, 'f': 'string'}, 'z': [{'z1': 10}, {'z1': 20}]}

def test_get1(self):

assert 10 == get_one_json_node(self.js, 'a')

assert 10 == get_one_json_node(self.js, 'e')

def test_get2(self):

assert 10 == get_one_json_node(self.js, 'c.e')

通过pytest测试发现,test_get2测试失败。原因是有多个e节点,我们需要支持c.e的访问,于是再封装一个上层方法:

def get_json_node(data: dict, field: str):

node_path = field.split(".")

node = data

find = False

for it in node_path:

node = get_one_json_node(node, it)

if not node:

return None

else:

find = True

if find:

return node

else:

return None

测试代码:

class TestJsonGet:

js = {'a': 10, 'b': 20, 'c': {'e': 10, 'f': 'string'}, 'c1': {'e': {'f1': 30}, 'f': 'string'},

'c2': {'e': 10, 'f': 'string'}, 'z': [{'z1': 10}, {'z1': 20}]}

def test_get3(self):

assert 10 == get_json_node(self.js, 'a')

assert 10 == get_json_node(self.js, 'e')

assert 10 == get_json_node(self.js, 'c.e')

def test_get4(self):

assert 10 == get_json_node(self.js, 'z.z1')

这次pytest测试就全部通过了(实际上,我也测试了数组节点的访问。为了简单起见,匹配第一个节点时get就直接返回,无需返回所有匹配节点)。节点的get方法执行时间0.1ms左右,相比jsonpath-ng提升近百倍了,可以接受。测试结果如下:

pytest -s test/test_jsonhelper.py::TestJsonGet

======================================================= test session starts =======================================================

platform darwin -- Python 3.8.2, pytest-6.1.2, py-1.9.0, pluggy-0.13.1

rootdir: /Users/xxx/data/code/python

collected 4 items

test/test_jsonhelper.py ..func:{get_json_node} exec time is:{0.00317} ms

func:{get_json_node} exec time is:{0.08789} ms

func:{get_json_node} exec time is:{0.00195} ms

.func:{get_json_node} exec time is:{0.00317} ms

节点的写入

同理,对于json节点设置可以参考get方法,写两个函数,具体代码如下:

def set_one_json_node(data: dict, field: str, value):

if isinstance(data, list):

for it in data:

ret = set_one_json_node(it, field, value)

if ret is not None:

return ret

if not isinstance(data, dict):

return None

if field in data.keys():

data[field] = value

return data

for it in data.keys():

ret = set_one_json_node(data[it], field, value)

if ret:

return ret

return None

def set_json_node(data: dict, field: str, value):

pos = field.find('.')

if pos != -1:

parent = field[0:pos]

node = get_json_node(data, parent)

if node is None:

return None

else:

return set_one_json_node(node, field[pos + 1:], value)

else:

return set_one_json_node(data, field, value)

测试代码如下:

class TestJsonSet:

js = {'a': 10, 'b': 20, 'c': {'e': 10, 'f': 'string'}, 'c1': {'e': {'f1': 30}, 'f': 'string'},

'c2': {'e': 10, 'f': 'string'}, 'z': [{'z1': 10}, {'z1': 20}]}

def test_set1(self):

js = copy.deepcopy(self.js)

set_one_json_node(js, 'a', 20)

set_one_json_node(js, 'e', 30)

assert 20 == get_one_json_node(js, 'a')

assert 30 == get_one_json_node(js, 'e')

def test_set2(self):

js = copy.deepcopy(self.js)

set_one_json_node(js, 'c.e', 20)

assert None == get_one_json_node(self.js, 'c.e')

def test_set3(self):

js = copy.deepcopy(self.js)

set_json_node(js, 'a', 20)

set_json_node(js, 'e', 30)

assert 20 == get_json_node(js, 'a')

assert 30 == get_json_node(js, 'e')

set_json_node(js, 'c.e', 40)

assert 40 == get_json_node(js, 'c.e')

def test_set4(self):

js = copy.deepcopy(self.js)

set_json_node(js, 'z.z1', 100)

assert 100 == get_json_node(js, 'z.z1')

pytest测试,set方法的执行时间和get方法执行时间类似,没有出现大的变动,相比jsonpath-ng也有巨大的提升,能够满足要求。运行结果如下:

pytest -s test/test_jsonhelper.py::TestJsonSet

======================================================= test session starts =======================================================

platform darwin -- Python 3.8.2, pytest-6.1.2, py-1.9.0, pluggy-0.13.1

rootdir: /Users/xxx/data/code/python

collected 4 items

test/test_jsonhelper.py func:{test_set1} exec time is:{0.03638} ms

..func:{set_json_node} exec time is:{0.00293} ms

func:{set_json_node} exec time is:{0.00293} ms

func:{get_json_node} exec time is:{0.00195} ms

func:{get_json_node} exec time is:{0.00195} ms

func:{get_json_node} exec time is:{0.00098} ms

func:{set_json_node} exec time is:{0.00513} ms

func:{get_json_node} exec time is:{0.00098} ms

.func:{get_json_node} exec time is:{0.00195} ms

func:{set_json_node} exec time is:{0.01514} ms

func:{get_json_node} exec time is:{0.02588} ms

讨论

我们简单实现了json访问的get和set方法,性能得到比较好的提升,对于频繁访问json对象,可以不使用jsonpath-ng,直接使用上面的方法,从而规避jsonpath-ng的性能问题。

有的时候,对于自身的需求,一定要多思考,也许存在一种简单的方法,就能解决。不一定杀鸡得用牛刀,而且可能是一把生锈的牛刀,盲目使用第三方库,可能会走不少弯路。

结合之前的总结,完善JsonHelper类型,这样就可以使用了(当然代码的健壮性,还有不少工作,依据实际需求,大家有兴趣自行完善即可)。辅助类型JsonHelper代码如下:

class JsonHelper:

def __init__(self, buffer: dict):

self.__dict__['_buffer'] = buffer

def get(self, field: str):

ret = self.__get_json_node(self.__dict__['_buffer'], field)

if not ret:

raise Exception("field:{} is not exist".format(field))

else:

return ret

def set(self, field: str, value):

ret = self.__set_json_node(self.__dict__['_buffer'], field, value)

if ret is None:

raise Exception("field:{} is not exist".format(field))

return self

def __get_one_json_node(self, data: dict, field: str):

if isinstance(data, list):

for it in data:

ret = self.__get_one_json_node(it, field)

if ret is not None:

return ret

if not isinstance(data, dict):

return None

if field in data.keys():

return data[field]

for it in data.keys():

ret = self.__get_one_json_node(data[it], field)

if ret:

return ret

return None

def __get_json_node(self, data: dict, field: str):

node_path = field.split(".")

node = data

find = False

for it in node_path:

node = self.__get_one_json_node(node, it)

if not node:

return None

else:

find = True

if find:

return node

else:

return None

def __set_one_json_node(self, data: dict, field: str, value):

if isinstance(data, list):

for it in data:

ret = self.__set_one_json_node(it, field, value)

if ret is not None:

return ret

if not isinstance(data, dict):

return None

if field in data.keys():

data[field] = value

return data

for it in data.keys():

ret = self.__set_one_json_node(data[it], field, value)

if ret:

return ret

return None

def __set_json_node(self, data: dict, field: str, value):

pos = field.find('.')

if pos != -1:

parent = field[0:pos]

node = self.__get_json_node(data, parent)

if node is None:

return None

else:

return self.__set_one_json_node(node, field[pos + 1:], value)

else:

return self.__set_one_json_node(data, field, value)

python jsonpath set value_Python学习:json对象快速访问(续)相关推荐

  1. python重点知识 钻石_python——子类对象如何访问父类的同名方法

    1. 为什么只说方法不说属性 关于"子类对象如何访问父类的同名属性"是没有意义的.因为父类的属性子类都有,子类还有父类没有的属性,在初始化时,给子类对象具体化所有的给定属性,完全没 ...

  2. python获取包下的所有对象_Python访问COM对象的comtypes包简介

    Python访问COM对象的comtypes包简介 虽然pywin32包中包含的派遣高级客户端支持基于COM接口,它是无法访问COM 对象,除非他们在C + +封装代码自定义COM接口.在Python ...

  3. python jsonpath效率低_Python学习:jsonpath的性能问题

    问题 前面刚总结了,利用jsonpath可以快速访问和设置json对象节点值的帖子.没想到这么快就打脸了.python的jsonpath居然性能如此之差,简直无法接受. 今天其实就是抛一个问题,作为记 ...

  4. python jsonpath-rw_Python使用jsonpath-rw模块处理Json对象操作示例

    本文实例讲述了Python使用jsonpath-rw模块处理Json对象操作.分享给大家供大家参考,具体如下: 这两天在写一个爬虫,需要从网站返回的json数据提取一些有用的数据. 向url发起请求, ...

  5. js字符串怎么转python对象_python对象与json相互转换的方法 python中怎么把json对象转成字符串...

    在python中 如何实现将一个json字符串转化为对象 对象?json不能包含对象, 只能包含基本的数据类型, 键值对, 列表, 数字, 字符串等等 import jsond = {"k& ...

  6. python jsonpath库_Python爬虫(十六)_JSON模块与JsonPath

    本篇将介绍使用,更多内容请参考:Python学习指南 数据提取之JSON与JsonPATH JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它是的人们很容易 ...

  7. javascript学习之对象基础

    2019独角兽企业重金招聘Python工程师标准>>> javascript学习之对象基础 面向对象语言 面向对象语言需要向开发者提供四种基本能力 封装:把相关信息(数据和方法)存储 ...

  8. json对象、json字符串的区别和相互转换

    开发时,json字符串和json对象傻傻分不清楚,ajax异步请求后,会根据返回的data值判断请求是否成功,访问data.msg会提示'undefined',将data转换为json对象即可. 一. ...

  9. python中如何对复杂的json数据快速查找key对应的value值(使用JsonSearch包)

    前言 之前在实际的项目研发中,需要对一些复杂的json数据进行取值操作,由于json数据的层级很深,所以经常取值的代码会变成类似这样: value = data['store']['book'][0] ...

最新文章

  1. favicon支持的图片格式
  2. 老板:kill -9 的原理都不知道就敢去线上执行?明天不用来了
  3. GitHub 4.6 万星:Windows Terminal 预览版开放下载!
  4. ActiveX、OLE和COM介绍
  5. 香肠派对电脑版_香肠派对先行服s7赛季下载-香肠派对先行服s7赛季最新版下载...
  6. BERT跨模态之后:占领了视觉常识推理任务榜单TOP 2!
  7. php ftp login,关于php ftp_login()函数的10篇文章推荐
  8. ios 绘制线框_iOS开发 给View添加指定位置的边框线
  9. WSUS补丁更新相关命令及参数
  10. Linux驱动编程--基于I2C子系统的I2C驱动
  11. 制作单机俄罗斯方块游戏心得(二)
  12. “飞跃四十载 同发展·共繁荣”巡回展在江苏举行
  13. OGNL表达式用法详解
  14. Eclipse快捷键 10个最有用的快捷键
  15. word文档找不到smartart_Word2016文档中插入SmartArt图形并添加文本的方法
  16. softice 常用操作
  17. Spring5-Bean的自动装配
  18. 电脑重装系统后U6连不上服务器了,重装系统后电脑怎么连不上网络?电脑连接网络步骤...
  19. 多项式乘积求极值点与拐点“比较快速”的方法:沉鱼落雁闭月羞花
  20. python中@用法详解

热门文章

  1. python个人微信支付接口_Python实现微信小程序支付功能
  2. CentOS7环境下搭建flume
  3. 中职计算机专业选修课程,《办好中职学校计算机专业的几点思考.doc
  4. LeetCode - Easy - Excel Sheet Column Title
  5. Java内部类的使用小结
  6. Alpine Linux:从漏洞发现到代码执行
  7. tomcat配置虚拟目录的方法
  8. STL学习系列五:Queue容器
  9. Centos系统服务FAQ汇总(下)
  10. eclipse各个版本的区别