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

使用Marshmallow验证嵌套字段列表时遇类型错误的解决方法

Marshmallow验证multipart请求嵌套列表失败的原因与解决办法

问题根源

你发送请求时用json.dumps()把hyperparams序列化成了JSON字符串,但Marshmallow的fields.List(fields.Nested(HyperParameterSchema))期望接收的是Python列表对象。

当使用requests.post发送包含files的请求时,请求会自动以multipart/form-data格式发送,此时data字典里的所有值都会被当作字符串处理。Marshmallow接收到hyperparams的JSON字符串后,会把它当成列表的第一个元素,自然触发"Invalid type."的验证错误——因为它期望的是列表里每个元素都是符合HyperParameterSchema的字典,而不是一个字符串。

解决方案

方案1:后端添加预加载钩子解析JSON字符串

在LearnerRequestSchema中用pre_load装饰器,把接收到的JSON字符串转成Python列表后再验证:

from marshmallow import Schema, fields, pre_load, ValidationError
import json

class HyperParameterSchema(Schema):
    name = fields.Str(required=True)
    data_type = fields.Str(required=True)
    default_value = fields.Str(required=True)

class LearnerRequestSchema(Schema):
    files = fields.Raw(type="file")
    name = fields.Str(required=True)
    description = fields.Str(required=True)
    supervision_scope = fields.Enum(SupervisionScope, by_value=True, required=True)
    task_type = fields.Enum(TaskType, by_value=True, required=True)
    tool = fields.Enum(Tool, by_value=True, required=True)
    learner_family_id = fields.Int(required=True)
    hyperparams = fields.List(fields.Nested(HyperParameterSchema))

    @pre_load
    def parse_hyperparams(self, data, **kwargs):
        hyperparam_str = data.get('hyperparams')
        if isinstance(hyperparam_str, str):
            try:
                data['hyperparams'] = json.loads(hyperparam_str)
            except json.JSONDecodeError:
                raise ValidationError("hyperparams字段格式无效,需为合法JSON字符串")
        return data

这个方案不需要修改请求端代码,直接在后端完成格式转换,适合现有请求逻辑不变的场景。

方案2:请求端按multipart格式传递嵌套列表

修改请求代码,把嵌套列表拆成符合multipart/form-data规范的字段命名(类似hyperparams[index][key]的格式),Flask会自动把这些字段解析成嵌套字典的列表:

response = requests.post(
    url="http://localhost:8080/api/learners",
    data={
        "name": "some_name",
        "description": "some_description",
        "supervision_scope": "supervised",
        "task_type": "classification",
        "tool": "scikit-learn",
        "learner_family_id": 1,
        # 按multipart规范传递嵌套列表
        "hyperparams[0][name]": "alpha",
        "hyperparams[0][data_type]": "float",
        "hyperparams[0][default_value]": "0.1",
        "hyperparams[1][name]": "l1_ratio",
        "hyperparams[1][data_type]": "float",
        "hyperparams[1][default_value]": "0.1",
    },
    files={
        "file": open("path_to_file", "rb")
    },
    headers=headers,
)

这个方案更符合multipart/form-data的原生数据格式,后端不需要额外解析逻辑,验证流程更规范。

补充说明

你用fields.Raw()能绕过错误,是因为Raw字段不做任何类型校验,直接接收原始字符串值,但这样就丢失了Marshmallow自动验证嵌套结构的能力,需要手动解析和校验,所以不推荐长期使用。

内容的提问来源于stack exchange,提问作者Vinicius Kreischer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 02:44:55