我试图通过jQuery.param()
函数反序列化复杂数据。
我找到了python库jquery-unparam,但不幸的是它并没有很好地管理可能发生的那种复杂结构。
我做了一个涵盖所有可能性的例子,这里是数据:
sample = {
'some': 'value',
'with': True,
'but[]': ['contains', 'also', 'some', 'array'],
'or[0]': 'even',
'or[1]': 'numbered',
'or[2]': 'arrays',
'also[0][some]': 'with',
'also[0][sub]': 'complex',
'also[0][values]': 'structure',
'and[finally]': 'some',
'and[easier]': 'ones'
}
此数据是Flask中request.form.to_dict()
的结果。如果您需要,请按jQuery.param()
处理后的原始值:
"some=value&with=true&but%5B%5D=contains&but%5B%5D=also&but%5B%5D=some&but%5B%5D=array&or%5B0%5D=even&or%5B1%5D=numbered&or%5B2%5D=arrays&also%5B0%5D%5Bsome%5D=with&also%5B0%5D%5Bsub%5D=complex&also%5B0%5D%5Bvalues%5D=structure&and%5Bfinally%5D=some&and%5Beasier%5D=ones"
现在,如果您直接从Python库运行代码,您将得到以下结果:
{"and": {"finally": "some", "easier": "ones"}, "some": "value", "but": ["contains", "also", "some", "array"], "also": {"0": {"values": "structure", "some": "with", "sub": "complex"}}, "with": "true", "or": {"1": "numbered", "0": "even", "2": "arrays"}}
问题是,密钥also
应该是一个列表,而不是一个字典,它应该是这样的:
{"and": {"finally": "some", "easier": "ones"}, "some": "value", "but": ["contains", "also", "some", "array"], "also": [{"values": "structure", "some": "with", "sub": "complex"}], "with": "true", "or": {"1": "numbered", "0": "even", "2": "arrays"}}
但是我坚持让它成功。
到目前为止我的代码(从库中更改为直接从Flask的to_dict代码开始):
#!/usr/bin/python
# -*- coding: utf-8 -*-
import re, json
def parse_key_pair(key, val):
groups = re.findall(r"\[.*?\]", key)
groups_joined = ''.join(groups)
if key[-len(groups_joined):] == groups_joined:
key = key[:-len(groups_joined)]
for group in reversed(groups):
if group == '[]':
val = val
else:
# I've implemented this to transform to list, but the result is not good
try:
int(group.replace('[', '').replace(']', ''))
val = [val]
except ValueError:
val = {group[1:-1]: val}
return {key: val}
def merge_two_structs(s1, s2):
if isinstance(s1, list) and \
isinstance(s2, list):
return s1 + s2
if isinstance(s1, dict) and \
isinstance(s2, dict):
retval = s1.copy()
for key, val in s2.iteritems():
if retval.get(key) is None:
retval[key] = val
else:
retval[key] = merge_two_structs(retval[key], val)
return retval
return s2
def merge_structs(structs):
if len(structs) == 0:
return None
if len(structs) == 1:
return structs[0]
first, rest = structs[0], structs[1:]
return merge_two_structs(first, merge_structs(rest))
def parse_form(pair_strings):
key_pairs = [parse_key_pair(x, pair_strings[x]) for x in pair_strings]
return merge_structs(key_pairs)
sample = {
'some': 'value',
'with': True,
'but[]': ['contains', 'also', 'some', 'array'],
'or[0]': 'even',
'or[1]': 'numbered',
'or[2]': 'arrays',
'also[0][some]': 'with',
'also[0][sub]': 'complex',
'also[0][values]': 'structure',
'and[finally]': 'some',
'and[easier]': 'ones'
}
print (json.dumps(parse_form(sample)))
如果你可以帮我解决如何获取列表而不是数组的dict,那将是完美的!
当然,如果已经有其他图书馆能够很好地完成这项工作,我全都是耳朵!
答案 0 :(得分:0)
上面的代码存在问题。
request.form.to_dict()
无法正确表示通过"item[]=XXX&item[]=YYY"
传递的项目。您只能获得一个item[]
...
所以为了解决这个问题,我已经将to_dict()
重写为:
from six import string_types
def convert_val(val):
if not isinstance(val, string_types):
return val
elif val == '':
return None
elif val.lower() in ('false', 'true'):
return True if val.lower() == 'true' else False
if val[0:1] == '{':
try:
return json.loads(val)
except ValueError:
pass
try:
return int(val)
except ValueError:
return val
def request_to_dict(params):
result = {}
for k in params:
items = params.getlist(k)
if len(items) == 1:
result[k] = convert_val(items[0])
else:
result[k] = items
return parse_form(result)
这将返回一个正确的字典,解析上述示例的特性,尽可能保留类型(布尔值,整数,json,列表或字符串),并且与Python 2和3兼容。