【发布时间】:2010-10-25 10:51:08
【问题描述】:
如何在 Python 中获取多维字典的 URL 编码版本?不幸的是,urllib.urlencode() 仅适用于单一维度。我需要一个能够递归编码字典的版本。
例如,如果我有以下字典:
{'a': 'b', 'c': {'d': 'e'}}
我想获取如下字符串:
a=b&c[d]=e
【问题讨论】:
如何在 Python 中获取多维字典的 URL 编码版本?不幸的是,urllib.urlencode() 仅适用于单一维度。我需要一个能够递归编码字典的版本。
例如,如果我有以下字典:
{'a': 'b', 'c': {'d': 'e'}}
我想获取如下字符串:
a=b&c[d]=e
【问题讨论】:
好的人。我自己实现的:
import urllib
def recursive_urlencode(d):
"""URL-encode a multidimensional dictionary.
>>> data = {'a': 'b&c', 'd': {'e': {'f&g': 'h*i'}}, 'j': 'k'}
>>> recursive_urlencode(data)
u'a=b%26c&j=k&d[e][f%26g]=h%2Ai'
"""
def recursion(d, base=[]):
pairs = []
for key, value in d.items():
new_base = base + [key]
if hasattr(value, 'values'):
pairs += recursion(value, new_base)
else:
new_pair = None
if len(new_base) > 1:
first = urllib.quote(new_base.pop(0))
rest = map(lambda x: urllib.quote(x), new_base)
new_pair = "%s[%s]=%s" % (first, ']['.join(rest), urllib.quote(unicode(value)))
else:
new_pair = "%s=%s" % (urllib.quote(unicode(key)), urllib.quote(unicode(value)))
pairs.append(new_pair)
return pairs
return '&'.join(recursion(d))
if __name__ == "__main__":
import doctest
doctest.testmod()
不过,我很想知道是否有更好的方法来做到这一点。我不敢相信 Python 的标准库没有实现这一点。
【讨论】:
这样的?
a = {'a': 'b', 'c': {'d': 'e'}}
url = urllib.urlencode([('%s[%s]'%(k,v.keys()[0]), v.values()[0] ) if type(v)==dict else (k,v) for k,v in a.iteritems()])
url = 'a=b&c%5Bd%5D=e'
【讨论】:
基于@malaney的代码,我认为下面的代码很好地模拟了PHP函数http_build_query()。
#!/usr/bin/env python3
import urllib.parse
def http_build_query(data):
parents = list()
pairs = dict()
def renderKey(parents):
depth, outStr = 0, ''
for x in parents:
s = "[%s]" if depth > 0 or isinstance(x, int) else "%s"
outStr += s % str(x)
depth += 1
return outStr
def r_urlencode(data):
if isinstance(data, list) or isinstance(data, tuple):
for i in range(len(data)):
parents.append(i)
r_urlencode(data[i])
parents.pop()
elif isinstance(data, dict):
for key, value in data.items():
parents.append(key)
r_urlencode(value)
parents.pop()
else:
pairs[renderKey(parents)] = str(data)
return pairs
return urllib.parse.urlencode(r_urlencode(data))
if __name__ == '__main__':
payload = {
'action': 'add',
'controller': 'invoice',
'code': 'debtor',
'InvoiceLines': [
{'PriceExcl': 150, 'Description': 'Setupfee'},
{'PriceExcl':49.99, 'Description':'Subscription'}
],
'date': '2016-08-01',
'key': 'Yikes&ersand'
}
print(http_build_query(payload))
payload2 = [
'item1',
'item2'
]
print(http_build_query(payload2))
【讨论】:
上述解决方案仅适用于深度
#!/usr/bin/env python
import sys
import urllib
def recursive_urlencode(data):
def r_urlencode(data, parent=None, pairs=None):
if pairs is None:
pairs = {}
if parent is None:
parents = []
else:
parents = parent
for key, value in data.items():
if hasattr(value, 'values'):
parents.append(key)
r_urlencode(value, parents, pairs)
parents.pop()
else:
pairs[renderKey(parents + [key])] = renderVal(value)
return pairs
return urllib.urlencode(r_urlencode(data))
def renderKey(parents):
depth, outStr = 0, ''
for x in parents:
str = "[%s]" if depth > 0 else "%s"
outStr += str % renderVal(x)
depth += 1
return outStr
def renderVal(val):
return urllib.quote(unicode(val))
def main():
print recursive_urlencode(payload)
if __name__ == '__main__':
sys.exit(main())
【讨论】:
我认为下面的代码可能是你想要的
导入 urllib.parse def url_encoder(参数): g_encode_params = {} def _encode_params(参数,p_key=None): 编码参数 = {} 如果是实例(参数,字典): 输入参数: encode_key = '{}[{}]'.format(p_key,key) 编码参数[编码键] = 参数[键] elif isinstance(参数,(列表,元组)): 对于偏移量,枚举中的值(参数): encode_key = '{}[{}]'.format(p_key, offset) 编码参数[编码键] = 值 别的: g_encode_params[p_key] = 参数 对于 encode_params 中的键: 值 = 编码参数 [键] _encode_params(值,键) 如果是实例(参数,字典): 输入参数: _encode_params(参数[键],键) 返回 urllib.parse.urlencode(g_encode_params) 如果 __name__ == '__main__': 参数 = {'name': 'interface_name', 'interfaces': [{'interface': 'inter1'}, {'interface': 'inter2'}]} 打印(url_encoder(参数))输出是
接口%5B1%5D%5Binterface%5D=inter2&name=interface_name&interfaces%5B0%5D%5Binterface%5D=inter1看起来像
接口[1][接口]=inter2&name=interface_name&interfaces[0][接口]=inter1PS:你可能想用OrderDict 代替上面的dict
【讨论】:
json.dumps 和 json.loads 呢?
d = {'a': 'b', 'c': {'d': 'e'}}
s = json.dumps(d) # s: '{"a": "b", "c": {"d": "e"}}'
json.loads(s) # -> d
【讨论】:
这个简化版怎么样:
def _clean(value):
return urllib.quote(unicode(value))
'&'.join([ v for val in [[ "%s[%s]=%s"%(k,ik, _(iv))
for ik, iv in v.items()] if type(v)==dict else ["%s=%s"%(k,_(v))]
for k,v in data.items() ]
for v in val ])
我同意它不可读,也许使用 itertools.chain 而不是另一个列表推导可以更好地完成列表的扁平化。
这只会更深 1 级,如果您根据级别添加一些逻辑来管理 N 个“[%s]”,您的可以更深 N 级,但我想没有必要
【讨论】:
如果你想将 python dict/list/nested 转换为 PHP Array,如 urlencoded 字符串。
在python中,大部分你想转换为urlencoded的数据类型可能是:dictlisttuplenested of them,Like
a = [1, 2]
print(recursive_urlencode(a))
# 0=1&1=2
a2 = (1, '2')
print(recursive_urlencode(a2))
# 0=1&1=2
b = {'a': 11, 'b': 'foo'}
print(recursive_urlencode(b))
# a=11&b=foo
c = {'a': 11, 'b': [1, 2]}
print(recursive_urlencode(c))
# a=11&b[0]=1&b[1]=2
d = [1, {'a': 11, 'b': 22}]
print(recursive_urlencode(d))
# 0=1&1[a]=11&1[b]=22
e = {'a': 11, 'b': [1, {'c': 123}, [3, 'foo']]}
print(recursive_urlencode(e))
# a=11&b[0]=1&b[1][c]=123&b[2][0]=3&b[2][1]=foo
【讨论】:
get_encoded_url_params() 函数将字典作为参数并返回字典的 url 编码形式。
def get_encoded_url_params(d):
"""URL-encode a nested dictionary.
:param d = dict
:returns url encoded string with dict key-value pairs as query parameters
e.g.
if d = { "addr":{ "country": "US", "line": ["a","b"] },
"routing_number": "011100915", "token": "asdf"
}
:returns 'addr[country]=US&addr[line][0]=a&addr[line][1]=b&routing_number=011100915&token=asdf'
or 'addr%5Bcountry%5D=US&addr%5Bline%5D%5B0%5D=a&addr%5Bline%5D%5B1%5D=b&routing_number=011100915&token=asdf'
(which is url encoded form of the former using quote_plus())
"""
def get_pairs(value, base):
if isinstance(value, dict):
return get_dict_pairs(value, base)
elif isinstance(value, list):
return get_list_pairs(value, base)
else:
return [base + '=' + str(value)]
# use quote_plus() to get url encoded string
# return [quote_plus(base) + '=' + quote_plus(str(value))]
def get_list_pairs(li, base):
pairs = []
for idx, value in enumerate(li):
new_base = base + '[' + str(idx) + ']'
pairs += get_pairs(value, new_base)
return pairs
def get_dict_pairs(d, base=''):
pairs = []
for key, value in d.items():
new_base = key if base == '' else base + '[' + key + ']'
pairs += get_pairs(value, new_base)
return pairs
return '&'.join(get_dict_pairs(d))
【讨论】: