如何用Marshmallow指定rec_biblio中activity.category始终为数组?
解决方案:Marshmallow 保留Dict结构同时验证嵌套字段
你可以通过两种方式实现需求:既保持rec_biblio为字典结构,又强制rec_biblio.activity.category为数组类型。
方法一:使用嵌套Schema验证指定字段
这种方式符合Marshmallow的原生设计,通过定义小型嵌套Schema约束特定字段,其余字段保留字典的灵活性。
from marshmallow import Schema, fields # 定义activity字段的验证Schema class ActivitySchema(Schema): id = fields.Str() # 可选:如果需要验证id字段可保留,否则删除 category = fields.List(fields.Str(), required=True) # 强制为字符串数组,required参数可选 # 主Schema class BiblioMetadataSchema(Schema): """Schema for the biblio metadata.""" rec_biblio = fields.Nested(Schema.from_dict({ "activity": fields.Nested(ActivitySchema), # 用"*"匹配所有未明确指定的字段,保留原始结构 "*": fields.Raw() }))
说明
Schema.from_dict用于动态创建Schema,"*"通配符匹配所有未定义的字段,fields.Raw()确保这些字段保留原始数据类型。ActivitySchema专门约束activity下的category字段,确保它是字符串数组,不影响其他字段的结构。
方法二:使用pre_load钩子做验证与转换
如果只需要处理category字段,钩子函数更灵活,还能自动将单个值转换为数组(适配Elasticsearch可能返回的非数组场景)。
from marshmallow import Schema, fields, ValidationError class BiblioMetadataSchema(Schema): """Schema for the biblio metadata.""" rec_biblio = fields.Dict() @pre_load def ensure_category_is_list(self, data, **kwargs): rec_biblio = data.get("rec_biblio", {}) activity = rec_biblio.get("activity", {}) category = activity.get("category") if category is not None: # 若category不是数组,尝试转换为数组 if not isinstance(category, list): if isinstance(category, (str, int, float)): activity["category"] = [category] else: raise ValidationError("category必须是数组或可转换为数组的单个值") # 验证数组元素类型为字符串 for item in category: if not isinstance(item, str): raise ValidationError("category数组中的每个元素必须是字符串") return data
说明
pre_load钩子在数据被Schema加载前执行,可在这里修改或验证数据。- 代码包含自动转换逻辑:如果
category是单个字符串/数字,会自动转为数组;如果是其他非数组类型,直接抛出验证错误。
使用示例
# 验证合法数据 valid_data = { "rec_biblio": { "_id": "952822", "_legislation": "456/2012", "_lib_id": "1", "_updated": "2000-01-01T00:00:00.0000000Z", "_version": "21007", "activity": { "id": "74154", "category": ["AFS", "DUT"] } } } schema = BiblioMetadataSchema() result = schema.load(valid_data) print(result) # 验证通过,输出原始数据 # 测试单个值转数组 single_category_data = { "rec_biblio": { "activity": { "category": "AFS" } } } result = schema.load(single_category_data) print(result["rec_biblio"]["activity"]["category"]) # 输出: ["AFS"] # 测试非法数据 invalid_data = { "rec_biblio": { "activity": { "category": {"foo": "bar"} } } } try: schema.load(invalid_data) except ValidationError as err: print(err.messages) # 输出错误提示
内容的提问来源于stack exchange,提问作者Peter Válek
相关产品推荐
相关产品推荐

