PHPの http_build_query と Pythonの urlencode

シンプルなkey=valueの形式なら何を使っても同じ結果になるけど、ネストしたデータ構造だと結果が違う。

stackoverflow.com

元々はjQueryのparam()とPythonのurlencodeで結果が違うなーと思って(jQueryPHPと同じ結果になる)調べてたけど、どっちでもサーバー側では問題なく解釈されるみたい。まあサーバーの実装にもよるだろうけど。。

import requests
import urllib

def flatten(dictionary, parent_key=None):
    items = []
    for key, value in dictionary.items():
        new_key = "{}[{}]".format(str(parent_key), key) if parent_key else key
        if isinstance(value, dict):
            items.extend(flatten(value, new_key).items())
        elif isinstance(value, list) or isinstance(value, tuple):
            for k, v in enumerate(value):
                items.extend(flatten({str(k): v}, new_key).items())
        else:
            items.append((new_key, value))
    return dict(items)

form_data = {
    "k1" : "v1",
    "k2" : {
        "k2_1" : "v2_1",
        "k2_2" : "v2_2",
    },
    "k3" : ["v3_1", "v3_2", "v3_3", "v3_4"]
}

# urlencode
print(urllib.parse.urlencode(form_data))

# http_build_query compatible
print(urllib.parse.urlencode(flatten(form_data)))
$ python a.py  | nkf --url-input
k1=v1&k2={'k2_1':+'v2_1',+'k2_2':+'v2_2'}&k3=['v3_1',+'v3_2',+'v3_3',+'v3_4']
k1=v1&k2[k2_1]=v2_1&k2[k2_2]=v2_2&k3[0]=v3_1&k3[1]=v3_2&k3[2]=v3_3&k3[3]=v3_4

jQueryでは

decodeURI($.param(form_data))
"k1=v1&k2[k2_1]=v2_1&k2[k2_2]=v2_2&k3[]=v3_1&k3[]=v3_2&k3[]=v3_3&k3[]=v3_4"

jQueryのparam()は配列のインデックスを入れないの?

PHP環境がないので生のhttp_build_query()がどうなってるのか分からないけど、
とりあえず今回は配列データを使わないので気にしないことにした。