You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:51:08