2010-10-25 7 views
6

Comment puis-je obtenir une version codée en URL d'un dictionnaire multidimensionnel en Python? Malheureusement, urllib.urlencode() ne fonctionne que dans une seule dimension. J'aurais besoin d'une version capable de coder récursivement le dictionnaire.urlencode un dictionnaire multidimensionnel en python

Par exemple, si je le dictionnaire suivant:

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

Je veux obtenir la chaîne suivante:

a=b&c[d]=e 

Répondre

8

OK personnes. Je l'ai implémenté moi-même:

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

Encore, je serais intéressé de savoir s'il y a une meilleure manière de faire ceci. Je ne peux pas croire que la bibliothèque standard de Python ne l'implémente pas.

+7

Pourquoi le devrait-il? Ce n'est en aucun cas un format standard. La chose avec les crochets est une particularité spécifique à PHP qui n'est pas présente dans la plupart des autres langages/frameworks ou standards web. – bobince

+4

Il ne s'agit peut-être pas d'un standard pur-macaroni soutenu par l'IETF/W3C, mais il est si couramment utilisé de nos jours qu'il n'est pas inclus dans la bibliothèque standard de Python. J'ai développé des applications web dans un certain nombre de plates-formes et de langues, et cela a toujours été la convention. Et cela inclut les environnements basés sur Python tels que Django: donc non, ce n'est plus seulement une chose PHP. – pablobm

+1

Juste pour que les gens qui viennent ici plus tard soient au courant, ce code est proche du travail mais ne fonctionne pas pour les objets avec> 1 niveau d'imbrication. Ainsi, les objets avec un niveau d'imbrication supérieur à 1 sont ramenés en haut du hachage. –

3

Quelque chose comme ça?

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

Peur pas. Veuillez noter la différence entre 'a = b & c [d] = e' et votre proposition 'a = b & c% 5Bd% 5D = e'. Cette fuite ne devrait pas être là. – pablobm

+0

Ensuite, vous devrez parcourir le dictionnaire comme je l'ai fait et urlencode chaque élément séparément ... – eumiro

+0

Cela prend seulement la première valeur intérieure dans un dict mais j'aime la façon dont vous l'avez fait donc +1 pour vous hehe – Hassek

0

qu'en est-il de json.dumps et de json.loads?

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

Je veux est d'encoder l'URL. Pas plus, pas moins – pablobm

0

ce que sur cette version simplifiée:

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

Je suis d'accord est illisible, aplatissement peut-être la liste peut être mieux fait avec itertools.chain au lieu d'une autre compréhension de la liste.

Cela ne va 1 niveau plus profond, le vôtre peut aller N niveaux plus profond si vous ajoutez une certaine logique pour gérer le nombre N de « [% s] » en fonction du niveau, mais je suppose est pas necesary

2

La solution ci-dessus ne fonctionne que pour les tableaux avec la profondeur < 2. Le code ci-dessous urlencode correctement un tableau multidimensionnel de n'importe quelle profondeur.

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

Cette fonction semble passer la chaîne à travers unicode deux fois. Donc un '!' se transforme en '% 25% 21' et non simplement en '% 21' – deweydb

1

Basé sur le code de @malaney, je pense que le code ci-dessous émule la fonction PHP http_build_query() très bien.

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

Je pense que le code ci-dessous peut être ce que vous voulez

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

la sortie est

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

qui ressemble à

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

PS: vous pouvez utiliser OrderDict pour remplacer dict ci-dessus

-1

Si vous voulez convertir python dict/list/imbriqué en tableau PHP comme une chaîne urlencodée.

En python, la plupart des données type que vous voulez convertir urlencoded peut-être: dictlisttuplenested of them, comme

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

post-scriptum un peu de code de: https://stackoverflow.com/a/4014164/2752670

Questions connexes