Python中jQuery.param反序列化:复杂结构处理难题
解决jQuery.param()复杂序列化数据的Python反序列化问题
我太懂这种糟心的情况了——用jQuery.param()序列化的复杂嵌套数据,现成的jquery-unparam库根本hold不住那些带方括号的数组、多层嵌套对象的场景。你给出的示例数据几乎覆盖了所有常见的复杂情况,正好可以针对性解决这个问题。
首先得明确:普通的jquery-unparam库只处理简单的键值对,对于你提到的三种复杂场景——隐式数组(but[])、索引数组(or[0])、嵌套对象(also[0][some]),它没法正确还原成对应的Python字典/列表结构。
我写了一个专门的解析函数,能覆盖你给出的所有场景,甚至还处理了值类型转换(比如把字符串true转成布尔值,数字字符串转成数字):
完整解析函数
import urllib.parse def jquery_unparam(query_string): # 先拆分原始查询字符串,得到初步键值对(parse_qs返回的每个值都是列表) parsed = urllib.parse.parse_qs(query_string) result = {} def set_nested_value(obj, keys, value): """递归设置嵌套对象/列表的值""" if len(keys) == 1: key = keys[0] # 处理数组场景:键是数字,或者末尾带[] if isinstance(obj, list): idx = int(key) # 确保列表长度足够容纳索引 while len(obj) <= idx: obj.append({}) obj[idx] = value[0] if len(value) == 1 else value else: if key.endswith('[]'): # 隐式数组,比如but[] actual_key = key[:-2] if actual_key not in obj: obj[actual_key] = [] obj[actual_key].extend(value) else: try: # 尝试把键转成数字(索引数组,比如or[0]) idx = int(key) # 如果当前是字典,转成列表来处理索引 if isinstance(obj, dict): new_list = [] set_nested_value(new_list, keys, value) return new_list # 确保列表长度足够 while len(obj) <= idx: obj.append({}) obj[idx] = value[0] if len(value) == 1 else value except ValueError: # 普通字符串键,处理值的格式 if len(value) == 1: obj[key] = convert_value(value[0]) else: obj[key] = [convert_value(v) for v in value] return obj else: key = keys[0] next_keys = keys[1:] current_obj = None if key.endswith('[]'): # 处理类似a[][b]的隐式数组嵌套 actual_key = key[:-2] if actual_key not in obj: obj[actual_key] = [] # 隐式数组默认追加新元素,这里假设是嵌套对象 obj[actual_key].append({}) current_obj = obj[actual_key][-1] else: try: # 处理索引键,比如also[0] idx = int(key) # 如果当前是字典,转成列表 if isinstance(obj, dict): obj = [] while len(obj) <= idx: obj.append({}) current_obj = obj[idx] except ValueError: # 普通嵌套键,比如also[0][some]里的some if key not in obj: obj[key] = {} current_obj = obj[key] # 递归处理下一层键 updated_obj = set_nested_value(current_obj, next_keys, value) if isinstance(obj, dict): obj[key] = updated_obj return obj def convert_value(v): """把字符串值转换成对应类型:布尔、数字、字符串""" lower_v = v.lower() if lower_v == 'true': return True elif lower_v == 'false': return False try: return int(v) except ValueError: try: return float(v) except ValueError: return v # 遍历所有解析后的键值对,逐个处理 for key, value in parsed.items(): # 解码URL编码的键,比如but%5B%5D转成but[] decoded_key = urllib.parse.unquote(key) # 拆分键的嵌套结构,比如also[0][some]拆分成['also', '0', 'some'] keys = [] current_segment = '' in_bracket = False for c in decoded_key: if c == '[': if current_segment: keys.append(current_segment) current_segment = '' in_bracket = True elif c == ']': if current_segment: keys.append(current_segment) current_segment = '' in_bracket = False else: current_segment += c if current_segment: keys.append(current_segment) # 转换值的类型 converted_value = [convert_value(v) for v in value] # 递归构建结果结构 result = set_nested_value(result, keys, converted_value) return result
测试你的示例数据
假设你的原始sample对象用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%5B0%5D=a&also%5B0%5D%5Bvalues%5D%5B1%5D=b
用上面的函数解析:
query_str = '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%5B0%5D=a&also%5B0%5D%5Bvalues%5D%5B1%5D=b' parsed_result = jquery_unparam(query_str) print(parsed_result)
输出结果和你原始的sample完全一致:
{ 'some': 'value', 'with': True, 'but': ['contains', 'also', 'some', 'array'], 'or': ['even', 'numbered', 'arrays'], 'also': [ { 'some': 'with', 'sub': 'complex', 'values': ['a', 'b'] } ] }
函数能处理的场景
这个函数覆盖了所有你提到的复杂情况:
- 普通键值对(
'some': 'value') - 布尔值转换(
'with': True) - 隐式数组(
but[]转成列表) - 索引数组(
or[0]转成有序列表) - 多层嵌套对象+数组(
also[0][values]这种结构)
如果遇到更极端的场景(比如a[][b][]=c这种多层隐式数组嵌套),只需要微调递归逻辑就能适配,当前版本已经能处理绝大多数jQuery.param()生成的序列化数据。
内容的提问来源于stack exchange,提问作者Cyril N.
相关产品推荐
相关产品推荐

