Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用python编码多维字典_Python_Urlencode - Fatal编程技术网

用python编码多维字典

用python编码多维字典,python,urlencode,Python,Urlencode,如何在Python中获得多维字典的URL编码版本?不幸的是,urllib.urlencode()只能在单个维度中工作。我需要一个能够递归编码字典的版本 例如,如果我有以下词典: {'a': 'b', 'c': {'d': 'e'}} 我想获得以下字符串: a=b&c[d]=e 像这样的 a = {'a': 'b', 'c': {'d': 'e'}} url = urllib.urlencode([('%s[%s]'%(k,v.keys()[0]), v.values()[0] )

如何在Python中获得多维字典的URL编码版本?不幸的是,
urllib.urlencode()
只能在单个维度中工作。我需要一个能够递归编码字典的版本

例如,如果我有以下词典:

{'a': 'b', 'c': {'d': 'e'}}
我想获得以下字符串:

a=b&c[d]=e
像这样的

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'

那么json.dumps和json.loads呢

d = {'a': 'b', 'c': {'d': 'e'}}
s = json.dumps(d)  # s: '{"a": "b", "c": {"d": "e"}}'
json.loads(s)  # -> d

好的,各位。我自己实现了它:

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的标准库没有实现这一点。

这个简化版本怎么样:

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而不是另一个列表理解可以更好地展平列表


这只会更深一层,如果您根据级别添加一些逻辑来管理N个“[%s]”的话,您可以更深N层,但我想这并不是必需的,上面的解决方案只适用于深度小于2的阵列。下面的代码将正确地对任何深度的多维数组进行URL编码

#!/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())

基于@malaney的代码,我认为下面的代码很好地模拟了PHP函数
http\u build\u 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&ampersand'
    }
    print(http_build_query(payload))

    payload2 = [
        'item1',
        'item2'
    ]
    print(http_build_query(payload2))

我想下面的代码可能就是你想要的

import urllib.parse def url_encoder(params): g_encode_params = {} def _encode_params(params, p_key=None): encode_params = {} if isinstance(params, dict): for key in params: encode_key = '{}[{}]'.format(p_key,key) encode_params[encode_key] = params[key] elif isinstance(params, (list, tuple)): for offset,value in enumerate(params): encode_key = '{}[{}]'.format(p_key, offset) encode_params[encode_key] = value else: g_encode_params[p_key] = params for key in encode_params: value = encode_params[key] _encode_params(value, key) if isinstance(params, dict): for key in params: _encode_params(params[key], key) return urllib.parse.urlencode(g_encode_params) if __name__ == '__main__': params = {'name': 'interface_name', 'interfaces': [{'interface': 'inter1'}, {'interface': 'inter2'}]} print(url_encoder(params)) 导入urllib.parse def url_编码器(参数): g_encode_params={} 定义编码参数(参数,p_键=无): encode_params={} 如果isinstance(参数,dict): 对于输入参数: encode_key='{}[{}]'.格式(p_key,key) encode_params[encode_key]=params[key] elif isinstance(参数,(列表,元组)): 对于偏移量,枚举中的值(参数): encode_key='{}[{}]'.格式(p_key,offset) encode_参数[encode_键]=值 其他: g_encode_params[p_key]=params 对于输入编码参数: 值=编码参数[键] _编码参数(值、键) 如果isinstance(参数,dict): 对于输入参数: _编码参数(参数[key],key) 返回urllib.parse.urlencode(g_encode_参数) 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': params={'name':'interface_name','interfaces':[{'interface':'inter1'},{'interface':'inter2'}]} 打印(url_编码器(参数)) 输出是

interfaces%5B1%5D%5Binterface%5D=inter2&name=interface_name&interfaces%5B0%5D%5Binterface%5D=inter1 接口%5B1%5D%5B接口%5D=inter2&name=接口名称&interfaces%5B0%5D%5B接口%5D=inter1 看起来像什么

interfaces[1][interface]=inter2&name=interface_name&interfaces[0][interface]=inter1 接口[1][interface]=inter2&name=interface\u name&interfaces[0][interface]=inter1
PS:如果您想将python dict/list/nested转换为类似URL编码字符串的PHP数组,您可能需要使用
OrderDict
替换上面的
dict

在python中,大多数要转换为URLCoded的数据类型可能是:
dict
list
tuple
嵌套的数据类型,如

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()将dict作为参数,并返回dict的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))

恐怕不行。请注意“a=b&c[d]=e”和您建议的“a=b&c%5Bd%5D=e”之间的差异。这种转义不应该存在。然后你必须像我一样迭代字典,并分别对每个项目进行URL编码…这只需要dict中的第一个内部值,但我喜欢你这样做的方式,所以为你+1。为什么应该这样做?这绝对不是一种标准格式。方括号是PHP特有的特性,在大多数其他语言/框架或任何web标准中都不存在。它可能不是一个纯粹的RFC描述的IETF/W3C支持的holy macaroni标准,但它现在非常常用,没有包含在Python的标准库中实在让我难以理解。我用许多平台和语言开发了web应用程序,这一直是惯例。这包括基于Python的环境,如Django:因此,不,它不再是PHP的唯一对象。正如稍后来到这里的人所意识到的,这段代码几乎可以工作,但不适用于嵌套级别>1的对象。因此嵌套级别>1的对象会被引导回哈希表的顶部。Eric Lubow:谢谢你的错误报告。我刚刚纠正了,现在应该没事了。太棒了。这救了我昨晚很晚。有点遗憾的是,urllib/urllib2和像请求这样的包装器在本机上不支持这一点。再次感谢您-我希望我能将此项向上投票10次。此函数似乎将字符串通过unicode两次。那么一个'!'变成“%25%21”不仅仅是“%21”我可能错了,但在我看来,json.dumps后跟url编码似乎是解决这个问题的一个很好的方法。这种跨框架(如Django等)的查询字符串的特殊构造是随着json的广泛使用而发展的,因此字符串表示为有效的json并不仅仅是偶然的。因此,json.dumps(支持递归)是更具Python风格的解决方案。最后的URL编码是这个问题的附带问题,应该由符合编码标准的库负责,因此解决方案最好还是留给urllib。您能解释一下您的答案吗?@BenjaminUrquhart函数get_encoded_URL_params()接受dict/map,并提供键值对作为URL编码参数。它类似于urllib.urlencode(),但也适用于原始dict中有dict或list的情况。我在comment-inside函数中提供了一个示例。