2010-10-25 1 views
6

Как получить URL-кодированную версию многомерного словаря в Python? К сожалению, urllib.urlencode() работает только в одном измерении. Мне нужна версия, способная рекурсивно кодировать словарь.urlencode многомерный словарь в python

Например, если у меня есть следующий словарь:

{'a': 'b', 'c': {'d': 'e'}} 

Я хочу получить следующую строку:

a=b&c[d]=e 

ответ

8

ОК люди. Я сам его реализовал:

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 не реализует это.

+7

Зачем? Это не является стандартным форматом. Вещь с квадратными скобками - специфическая для PHP особенность, не встречающаяся в большинстве других языков/фреймворков или любого веб-стандарта. – bobince

+4

Это не может быть чистый RFC-описанный стандарт IETF/W3C, поддерживаемый святой макароном, но он так часто используется в наши дни, что его включение в стандартную библиотеку Python не оспаривает мое понимание. Я разработал веб-приложения на нескольких платформах и языках, и это всегда было конвенцией. И это включает в себя среды на основе Python, такие как Django: так что нет, это больше не просто PHP. – pablobm

+1

Именно поэтому люди, которые приходят сюда позже, знают, что этот код близок к работе, но не работает для объектов с> 1 уровнем вложенности. Таким образом, объекты с> 1 уровнем вложенности загружаются обратно в верхнюю часть хэша. –

3

Что-то вроде этого?

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' 
+2

Боится не. Обратите внимание на разницу между «a = b & c [d] = e» и вашим предложенным «a = b & c% 5Bd% 5D = e». Этого побега не должно быть. – pablobm

+0

Тогда вам придется перебирать словарный словарь, как я, и urlencode каждый элемент отдельно ... – eumiro

+0

Это занимает первое внутреннее значение в dict, но мне нравится, как вы это делали, так что +1 для вас hehe – Hassek

0

Что относительно json.dumps и json.loads?

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

No. Я хочу, чтобы URL-кодирование. Не более, не менее – pablobm

0

, что об этой упрощенной версии:

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]» в зависимости от уровня, но я предполагаю, что это не то, что necesary

2

Вышеупомянутое решение работает только для массивов с глубиной < 2. Приведенный ниже код будет правильно отображать многомерный массив любой глубины.

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

Эта функция пропускает строку через unicode дважды. Итак, «!» превращается в «% 25% 21» не просто «% 21», – deweydb

1

на основе кода @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&ampersand' 
    } 
    print(http_build_query(payload)) 

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

Я думаю, что приведенный ниже код может быть то, что вы хотите

 
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)) 

выход

 
interfaces%5B1%5D%5Binterface%5D=inter2&name=interface_name&interfaces%5B0%5D%5Binterface%5D=inter1 

который похож

 
interfaces[1][interface]=inter2&name=interface_name&interfaces[0][interface]=inter1 

PS: вы можете используйте OrderDict для замены dict

-1

Если вы хотите конвертировать python dict/list/вложенный в PHP Array, как urlencoded string.

В питона, большинство данных типа вы хотите преобразовать в urlencoded может быть: dictlisttuplenested of them, Как

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 

https://github.com/Viky-zhang/to_php_post_arr

P.S. некоторый код от: https://stackoverflow.com/a/4014164/2752670

 Смежные вопросы

  • Нет связанных вопросов^_^