eval vs json 像字符串这样的字典的转换和解析

eval vs json conversion and parsing of a dictionary like string

自从我上一个 python 项目以来已经有一段时间了,所以我有点生疏了——请随时提供任何适当的建议或批评——所以我有几个关于 eval 和 JSON.

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

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}'}]

** 我遇到一个 post 表明 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 配置,具体取决于个人部署需要,所以我从来没有真正考虑过如何解析每个字典实例。考虑到我 运行 使用此解决方案遇到的障碍数量,我希望有人可以帮助设计一个解决方案,利用上面输出中找到的索引。

我很抱歉问了这么多,我确定我只是想多了一点,并期待了解我可以做些什么来改进。在此先感谢您的帮助!

通常,当 运行 带有 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.