eval vs json转换和解析字典之类的字符串

时间:2016-07-12 06:40:37

标签: python json dictionary eval abstract-syntax-tree

自从我上一次python项目以来已经有一段时间了,所以我有点生疏 - 随时可以提供任何建议或批评 - 所以我有一些关于eval和JSON的问题。 / p>

对于这个项目,我只限于Python 2.6默认库 - 我试图解析用于LDAP身份验证的专有Linux应用程序的数据库内容。用于查询数据库的特定命令并不严格,但我使用以下方法返回包含的输出:

process = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
stdout = process.communicate()[0]

输出:

[{'header_obj_idx': 32,
  'header_obj_state': 2,
  'header_obj_type': 48,
  'index': 2,
  'name': '',
  'obj_id': '8b14c165094d4cac81725227ce389277',
  'ldap_data': '{"search_filter":"(sAMAccountName={username})","roles":["admin:CN=SuperUsers,DC=example,DC=com","read_only:CN=Users,DC=example,DC=com"],"search_base":"OU=Users,DC=example,DC=com","server_url":["ldap://ad.example.com","ldaps://ad.example.com:3001"],"user_to_dn_rule":"{username}@example.com","bind_dn":"CN=Bind,OU=Users,DC=example,DC=com","timeout":1500,"bind_pw":"xxxxxxxx","cache_expire":86400,"ca_cert_file":null}'},
 {'header_obj_idx': 31,
  'header_obj_state': 2,
  'header_obj_type': 48,
  'index': 1,
  'name': '',
  'obj_id': 'b0efc7a3d38a4f70abec4f73f69124de',
  'ldap_data': '{"search_filter":"(sAMAccountName={username})","roles":["admin:CN=SuperUsers,DC=example,DC=com"],"search_base":"OU=Users,DC=example,DC=com","server_url":["ldap://169.254.0.1"],"user_to_dn_rule":null,"bind_dn":"CN=Bind,OU=Users,DC=example,DC=com","timeout":1500,"bind_pw":"xxxxxxxx","cache_expire":86400,"ca_cert_file":null}'}]

**我发现了一个帖子,表明shell=True可能会产生某些安全隐患,并且想知道是否有更好的解决方案?

同时使用ast.literal_evaljson.loads我已经能够成功解析单个密钥对,但我感觉我的多级转换不是必需的,并且相信可能有更好的方法?

def ldap_data(stdout):
    # evaluate object and return usable 'ldap_data' as dictionary
    _data = ast.literal_eval(stdout)[0]['ldap_data']
    return json.loads(_data)

ldap_data(stdout)['roles']

最后,当我启动这个项目时,我从未想过用户可能有多个ldap配置,具体取决于各个部署需求,因此,我从未真正考虑如何解析每个字典实例。考虑到我使用此解决方案遇到的障碍的数量,我希望有人可以帮助设计一个利用上面输出中找到的索引的解决方案。

我为这么多的问题而道歉,我确定我只是在思考这一点,并期待着学习我可能做些什么来改进。在此先感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

通常,当使用subprocess运行单个命令时,如果您输入命令名称并且每个选项都是单独的字符串,则不需要shell=True,即

['cmd', 'arg1', 'arg2']

需要shell=True来执行shell内部的命令,或者使用其他shell功能,如the docs中所述,但这不是问题。

至于解析那些数据,你不需要需要 ast.literal_eval,但是你需要修改引号以使该数据成为有效的JSON。这可以通过转义现有的双引号,然后将单引号转换为双引号来完成。一旦使用json.loads将修复后的数据解析为Python列表,您需要再次调用json.loads来提取LDAP词典。

import json

src = '''\
[{'header_obj_idx': 32,
  'header_obj_state': 2,
  'header_obj_type': 48,
  'index': 2,
  'name': '',
  'obj_id': '8b14c165094d4cac81725227ce389277',
  'ldap_data': '{"search_filter":"(sAMAccountName={username})","roles":["admin:CN=SuperUsers,DC=example,DC=com","read_only:CN=Users,DC=example,DC=com"],"search_base":"OU=Users,DC=example,DC=com","server_url":["ldap://ad.example.com","ldaps://ad.example.com:3001"],"user_to_dn_rule":"{username}@example.com","bind_dn":"CN=Bind,OU=Users,DC=example,DC=com","timeout":1500,"bind_pw":"xxxxxxxx","cache_expire":86400,"ca_cert_file":null}'},
 {'header_obj_idx': 31,
  'header_obj_state': 2,
  'header_obj_type': 48,
  'index': 1,
  'name': '',
  'obj_id': 'b0efc7a3d38a4f70abec4f73f69124de',
  'ldap_data': '{"search_filter":"(sAMAccountName={username})","roles":["admin:CN=SuperUsers,DC=example,DC=com"],"search_base":"OU=Users,DC=example,DC=com","server_url":["ldap://169.254.0.1"],"user_to_dn_rule":null,"bind_dn":"CN=Bind,OU=Users,DC=example,DC=com","timeout":1500,"bind_pw":"xxxxxxxx","cache_expire":86400,"ca_cert_file":null}'}]
'''

#Escape existing double quotes, and then convert single quotes to double quotes
data = json.loads(src.replace('"', '\\"').replace("'", '"'))
for d in data:
    ldap = json.loads(d['ldap_data'])
    print json.dumps(ldap, indent=4, sort_keys=True), '\n'

<强>输出

{
    "bind_dn": "CN=Bind,OU=Users,DC=example,DC=com", 
    "bind_pw": "xxxxxxxx", 
    "ca_cert_file": null, 
    "cache_expire": 86400, 
    "roles": [
        "admin:CN=SuperUsers,DC=example,DC=com", 
        "read_only:CN=Users,DC=example,DC=com"
    ], 
    "search_base": "OU=Users,DC=example,DC=com", 
    "search_filter": "(sAMAccountName={username})", 
    "server_url": [
        "ldap://ad.example.com", 
        "ldaps://ad.example.com:3001"
    ], 
    "timeout": 1500, 
    "user_to_dn_rule": "{username}@example.com"
} 

{
    "bind_dn": "CN=Bind,OU=Users,DC=example,DC=com", 
    "bind_pw": "xxxxxxxx", 
    "ca_cert_file": null, 
    "cache_expire": 86400, 
    "roles": [
        "admin:CN=SuperUsers,DC=example,DC=com"
    ], 
    "search_base": "OU=Users,DC=example,DC=com", 
    "search_filter": "(sAMAccountName={username})", 
    "server_url": [
        "ldap://169.254.0.1"
    ], 
    "timeout": 1500, 
    "user_to_dn_rule": null
} 

在Python 2.6.6上测试

请注意,ldap词典中的键(和值字符串)是Unicode字符串,JSON转储中表示为null的值实际上是None