
后端序列化【免费下载链接】marshmallowA lightweight library for converting complex objects to and from simple Python datatypes.项目地址https://gitcode.com/gh_mirrors/ma/marshmallow点击查看免费下载本篇技术指南围绕 marshmallow 仓库中docs/examples/目录下的三个官方实战示例展开package.json配置校验、Flask SQLAlchemy 引语 REST API、以及基于on_bind_field钩子的驼峰键名自动转换。读完本文你将掌握Schema.load校验与反序列化、自定义字段、data_key、unknown INCLUDE、嵌套字段、dump_only、输出字段过滤、pre_load预处理等核心能力的落地写法并能在本地用uv一键复现全部示例。示例概览与运行环境docs/examples/下共有三个可独立运行的示例脚本对应三篇独立文档示例文档脚本演示的核心特性Validating package.jsonexamples/package_json_example.pySchema.load校验与反序列化、自定义字段、data_key、unknown INCLUDEQuotes APIexamples/flask_example.py自定义校验、嵌套字段、dump_onlyTrue、only输出过滤、pre_load预处理Inflectionexamples/inflection_example.py通过Schema.on_bind_field钩子自动转换键名命名风格所有示例均依赖 uv 运行。每个脚本都声明了 PEP 723 内联元数据脚本头部的# /// script代码块其中明确列出所需依赖与最低 Python 版本uv会在首次运行时自动创建隔离环境并安装依赖无需手动pip install。例如 examples/package_json_example.py 声明了marshmallow与packaging17.0并要求requires-python 3.10examples/flask_example.py 则声明了flask、flask-sqlalchemy3.1.1、sqlalchemy2.0和marshmallow。示例一用 Schema 校验 package.json 配置需求场景与 Schema 设计marshmallow 最常见的用途之一就是按 Schema 校验配置文件。第一个示例为package.json定义了一套校验 Schema运行脚本后从stdin读取 JSON、校验并反序列化输出规范化的 Python 数据结构。其完整脚本见 examples/package_json_example.pySchema 定义如下from marshmallow import INCLUDE, Schema, ValidationError, fields class PackageSchema(Schema): name fields.Str(requiredTrue) version Version(requiredTrue) description fields.Str(requiredTrue) main fields.Str(requiredFalse) homepage fields.URL(requiredFalse) scripts fields.Dict(keysfields.Str(), valuesfields.Str()) license fields.Str(requiredTrue) dependencies fields.Dict(keysfields.Str(), valuesfields.Str(), requiredFalse) dev_dependencies fields.Dict( keysfields.Str(), valuesfields.Str(), requiredFalse, data_keydevDependencies, ) class Meta: # Include unknown fields in the deserialized output unknown INCLUDE该 Schema 展示了多个重要特性校验与反序列化一体Schema.load底层实现在 src/marshmallow/schema.py在校验通过后返回已反序列化的数据而非原样字符串。例如version字段会被转换为packaging.version.Version对象。data_key指定反序列化键名JSON 中 npm 生态习惯使用驼峰的devDependencies而 Python 端字段名是蛇形的dev_dependencies。通过data_keydevDependenciesSchema 会从输入数据的devDependencies键取值。从源码看无论是校验schema.py、schema.py还是序列化输出schema.pydata_key均优先于字段名决定外部键名且dump时若多个字段的data_key或名称互相冲突会抛出明确异常。unknown INCLUDE保留未知键package.json中通常含有大量 Schema 未声明的字段如keywords、author。在Meta中设置unknown INCLUDE后这些未知键不会被丢弃而是原样并入反序列化结果适合只要校验关键字段、其余字段照单全收的配置校验场景。marshmallow 还提供EXCLUDE默认丢弃未知键与RAISE遇到未知键抛ValidationError两种策略。Dict字段与嵌套键值类型约束scripts、dependencies等对象被建模为fields.Dict并同时约束键与值的类型例如keysfields.Str(), valuesfields.Str()强制字符串键 字符串值。自定义 Version 字段package.json的version使用 semver 语义化版本号。示例并未使用现成的fields.String而是基于packaging.version实现了自定义字段Version其中fields.Field[version.Version]是该字段的泛型标注表明它反序列化产出Version对象class Version(fields.Field[version.Version]): Version field that deserializes to a Version object. def _deserialize(self, value, *args, **kwargs): try: return version.Version(value) except version.InvalidVersion as e: raise ValidationError(Not a valid version.) from e def _serialize(self, value, *args, **kwargs): return str(value)_deserialize在load阶段被调用对应 src/marshmallow/fields.py 中Field基类_deserialize方法体系把字符串转为Version对象解析失败时抛出带中文上下文的ValidationError(Not a valid version.)该错误信息最终会以字典形式出现在error.messages中。_serialize在dump阶段把Version对象转回字符串保证序列化方向仍然输出 JSON 友好的数据。自定义字段是 marshmallow 扩展体系的核心能力之一更系统的说明见 custom_fields.rst。运行方式与两种输入输出脚本主体从sys.stdin读取 JSON调用PackageSchema().load(pkg)校验失败则打印错误并退出码 1if __name__ __main__: pkg json.load(sys.stdin) try: pprint(PackageSchema().load(pkg)) except ValidationError as error: print(ERROR: package.json is invalid) pprint(error.messages) sys.exit(1)合法输入examples/package.json{ name: dunderscore, version: 1.2.3, description: The Pythonic JavaScript toolkit, devDependencies: { pest: ^23.4.1 }, main: index.js, scripts: { test: pest }, license: MIT }执行命令与输出$ uv run examples/package_json_example.py examples/package.json {description: The Pythonic JavaScript toolkit, dev_dependencies: {pest: ^23.4.1}, license: MIT, main: index.js, name: dunderscore, scripts: {test: pest}, version: Version(1.2.3)}注意两点devDependencies被映射为字段名dev_dependenciesversion被自定义字段反序列化成了Version(1.2.3)对象这正是自定义字段的价值所在。非法输入examples/invalid_package.json{ name: dunderscore, version: INVALID, homepage: INVALID, description: The Pythonic JavaScript toolkit, license: MIT }version不是合法语义化版本、homepage不是合法 URL执行结果$ uv run examples/package_json_example.py examples/invalid_package.json ERROR: package.json is invalid {homepage: [Not a valid URL.], version: [Not a valid version.]}fields.URL内置校验与自定义字段的校验同时生效错误信息按字段名聚合成字典非常便于程序化处理和用户提示。示例二Flask SQLAlchemy 引语 REST API项目结构模型、Schema 与路由第二个示例是一个完整可运行的引语QuotesREST API完整代码见 examples/flask_example.py。它演示了 marshmallow 与 Web 框架、ORM 的典型协作模式Schema 负责输入校验与输出序列化SQLAlchemy 模型负责持久化。关键设计如下数据模型Authorid、first、last与Quoteid、content、author_id、posted_atQuote.author通过relationship关联Author并带backref(quotes, lazydynamic)数据库使用 SQLite 文件sqlite:////tmp/quotes.db启动时通过db.create_all()建表。Schema 实例化策略文件底部为每个 Schema 预建了单例并用manyTrue派生集合 Schemaauthor_schema AuthorSchema() authors_schema AuthorSchema(manyTrue) quote_schema QuoteSchema() quotes_schema QuoteSchema(manyTrue, only(id, content))四个核心特性逐一拆解1.dump_onlyTrue声明只读字段class AuthorSchema(Schema): id fields.Int(dump_onlyTrue) first fields.Str() last fields.Str() formatted_name fields.Method(format_name, dump_onlyTrue)id、formatted_name和QuoteSchema中的posted_at都标记为dump_only这些字段只在dump序列化输出时出现load时被忽略且不参与校验——正好对应数据库自增主键、服务端计算字段、服务端写入时间戳这三类客户端不该提供的数据。fields.Method(format_name, ...)则是序列化钩子dump时调用format_name(author)方法生成Peters, Tim形式的全名。2. 自定义校验函数def must_not_be_blank(data): if not data: raise ValidationError(Data not provided.) class QuoteSchema(Schema): id fields.Int(dump_onlyTrue) author fields.Nested(AuthorSchema, validatemust_not_be_blank) content fields.Str(requiredTrue, validatemust_not_be_blank) posted_at fields.DateTime(dump_onlyTrue)validate参数接受一个可调用对象返回值不为真即视为校验失败并抛ValidationError。这里对嵌套的author与必填的content都附加了非空白校验避免空字符串入库。3. 嵌套字段与校验错误合并fields.Nested(AuthorSchema)让引语对象在输入时嵌入完整的作者对象。当客户端 POST 时省略authormust_not_be_blank会触发错误以{author: [Data not provided.]}形式返回实际返回 HTTP 422见下文new_quote路由的异常处理。嵌套字段的详细用法见 nesting.rst。4.only参数过滤输出字段QuoteSchema(manyTrue, only(id, content))在序列化列表时只输出id和content隐藏author、posted_at等字段。这正是引语列表只需要展示内容、无需重复回显作者与时间戳的典型场景。pre_load请求体预处理QuoteSchema定义了一个关键的pre_load钩子对应 src/marshmallow/decorators.py 的pre_load装饰器完整语义见 pre_and_post_processing.rst# Allow client to pass authors full name in request body # e.g. {author: Tim Peters} rather than {first: Tim, last: Peters} pre_load def process_author(self, data, **kwargs): author_name data.get(author) if author_name: first, last author_name.split( ) author_dict {first: first, last: last} else: author_dict {} data[author] author_dict return data它允许客户端直接提交{author: Tim Peters, content: ...}这种扁平结构在真正进入字段级反序列化之前把作者全名拆成{first: Tim, last: Peters}字典。若作者不存在则置为空字典以触发后续的must_not_be_blank校验。pre_load处理发生在Schema.load内部、字段校验之前对应 schema.py 中 load 流程对pre_load处理器的调用点因此转换对调用方完全透明。路由与错误处理API 提供了 5 个端点GET /authors序列化全部作者authors_schema.dump(authors)。GET /authors/int:pk查单作者返回作者对象与他的全部引语quotes_schema.dump(author.quotes.all())查无此人返回 400。GET /quotes/返回全部引语的精简列表只含id、content。GET /quotes/int:pk查单条引语。POST /quotes/接收 JSONquote_schema.load(json_data)校验反序列化校验失败时把err.messages原样返回并给 422 状态码成功则复用或新建作者、写入引语并提交事务。dataclass app.route(/quotes/, methods[POST]) def new_quote(): json_data request.get_json() if not json_data: return {message: No input data provided}, 400 try: data quote_schema.load(json_data) except ValidationError as err: return err.messages, 422 ...注意这里quote_schema.load返回的数据已经经过pre_load拆分与嵌套反序列化data[author]是含first/last的字典可直接驱动 ORM 查询或建库。另外注意示例文档中的校验失败响应示例返回了字段错误字典{author: [Data not provided.]}而源码实际返回 422 状态码。动手运行 API依次执行$ uv run examples/flask_example.py启动后服务监听 5000 端口。官方示例使用 httpie 发送请求可先用 uv 安装$ uv tool install httpiePOST 几条引语$ http POST :5000/quotes/ authorTim Peters contentBeautiful is better than ugly. $ http POST :5000/quotes/ authorTim Peters contentNow is better than never. $ http POST :5000/quotes/ authorPeter Hintjens contentSimplicity is always better than functionality.校验失败时的响应故意省略 author$ http POST :5000/quotes/ contentI have no author { author: [ Data not provided. ] }GET 全部引语注意only(id, content)生效无 author 与时间戳$ http :5000/quotes/ { quotes: [ { content: Beautiful is better than ugly., id: 1 }, { content: Now is better than never., id: 2 }, { content: Simplicity is always better than functionality., id: 3 } ] }GET 某位作者及其引语$ http :5000/authors/1 { author: { first: Tim, formatted_name: Peters, Tim, id: 1, last: Peters }, quotes: [ { content: Beautiful is better than ugly., id: 1 }, { content: Now is better than never., id: 2 } ] }从响应可见dump_only的formatted_name由fields.Method动态生成作者列表的引语同样被only过滤为idcontent。这个示例完整覆盖了 marshmallow 在真实 Web 服务中最常见的全部配合模式也是理解 quickstart.rst 之后的最佳进阶素材。示例三用 on_bind_field 实现键名自动变形Inflection问题HTTP API 的驼峰键名很多 HTTP API 的对外 JSON 使用驼峰键firstName而 Python 代码习惯蛇形命名first_name。逐个给字段写data_key既繁琐又易漏。第三个示例examples/inflection_example.py演示了如何用一个基类彻底解决该问题。核心重写 on_bind_field 钩子Schema.on_bind_field在字段绑定到 Schema 时被调用一次接收字段名与字段对象定义见 schema.py。利用这个时机修改字段的data_key即可在加载/序列化两个方向上统一键名转换from marshmallow import Schema, fields def camelcase(s): parts iter(s.split(_)) return next(parts) .join(i.title() for i in parts) class CamelCaseSchema(Schema): Schema that uses camel-case for its external representation and snake-case for its internal representation. def on_bind_field(self, field_name, field_obj): field_obj.data_key camelcase(field_obj.data_key or field_name)camelcase(first_name)输出firstName首个下划线片段保留原样后续片段首字母大写后拼接。field_obj.data_key or field_name保证若字段已显式指定data_key优先保留显式值否则用字段名做转换。由于on_bind_field在 Schema 声明期统一执行后续所有继承CamelCaseSchema的子类自动获得驼峰外部表示无需重复编码。使用与运行结果class UserSchema(CamelCaseSchema): first_name fields.Str(requiredTrue) last_name fields.Str(requiredTrue) schema UserSchema() loaded schema.load({firstName: David, lastName: Bowie}) print(Loaded data:) print(loaded) dumped schema.dump(loaded) print(Dumped data:) print(dumped)运行$ uv run examples/inflection_example.py Loaded data: {first_name: David, last_name: Bowie} Dumped data: {firstName: David, lastName: Bowie}load把外部的驼峰键转换为 Python 侧蛇形键dump再把蛇形键还原为 API 要求的驼峰键——一套 Schema 同时服务两个方向。若还需要支持复数化、kebab-case 等更复杂的变形规则可借助第三方库如 inflection在camelcase函数内实现而不必改动 Schema 框架本身。从示例到源码三个特性的底层原理三个示例用到的核心机制在源码中都有明确对应Schema.load的校验与反序列化流程加载过程中先应用pre_load等处理钩子再按字段逐一调用_deserialize完成类型转换与校验错误统一汇集到error_store最终以字段名/data_key为键聚合到ValidationError.messages可对照 src/marshmallow/schema.py 的 load 实现与 src/marshmallow/error_store.py。data_key的作用点Schema 的序列化与校验路径中均以field_obj.data_key if field_obj.data_key is not None else field_name决定外部键名schema.py、schema.py、schema.py同时dump时会检测重复的data_key并抛错。这正是示例一devDependencies映射与示例三on_bind_field修改data_key得以生效的根本原因。自定义字段的_deserialize/_serialize契约Field基类src/marshmallow/fields.py通过_deserialize与_serialize两个钩子定义双向转换内置的Str、URL、Dict、DateTime、Int等类型均遵循同一契约示例一的Version字段正是对这一扩展点的直接利用。这三个示例也分别对应文档站上的独立章节validating_package_json.rst、quotes_api.rst 与 inflection.rst可与 custom_fields.rst、nesting.rst、pre_and_post_processing.rst 等参考文档配合阅读形成完整的知识闭环。赞分享后端序列化【免费下载链接】marshmallowA lightweight library for converting complex objects to and from simple Python datatypes.项目地址https://gitcode.com/gh_mirrors/ma/marshmallow点击查看免费下载相关推荐CodeIgniter Inflector Helper 完全指南单复数、驼峰命名与序数词转换实战CodeIgniter Inflector Helper 完全指南单复数、驼峰命名与序数词转换实战 本文围绕 CodeIgniter 3 框架内置的 Infl后端Web框架FastJSON字段命名策略PropertyNamingStrategy实现驼峰/下划线转换FastJSON字段命名策略PropertyNamingStrategy实现驼峰/下划线转换 1. 命名策略概述 在JSON序列化与反序列化过程中Java对序列化后端MJExtension驼峰命名转换终极指南轻松处理JSON与模型映射MJExtension驼峰命名转换终极指南轻松处理JSON与模型映射 MJExtension是一款快速、便捷且非侵入式的JSON与模型转换框架让你的模型类无开发工具上一篇3D ViewPager让你的Android应用拥有惊艳立体翻页效果下一篇floating-ui尺寸调整根据内容动态调整浮动元素大小创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考