ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Effect SchemaRepresentation 增强:`fromJsonSchemaMultiDocument` 支持带兄弟关键字的 `anyOf`/`oneOf` 组合

Effect SchemaRepresentation 增强:`fromJsonSchemaMultiDocument` 支持带兄弟关键字的 `anyOf`/`oneOf` 组合 Effect SchemaRepresentation 增强fromJsonSchemaMultiDocument支持带兄弟关键字的anyOf/oneOf组合【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code本篇文章围绕 Effect 开源仓库.repos/effect-smol中的一项 Schema 导入能力增强展开SchemaRepresentation.fromJsonSchemaMultiDocument现已支持解析同时携带anyOf/oneOf以及其它兄弟关键字如type、const、enum、$ref、allOf、properties等的 JSON Schema 节点。读完本文你将理解多文档 Schema 导入的完整模型、组合语义在源码中的实现方式以及如何用fromJsonSchemaMultiDocument把现实世界中“复合约束叠写”的 OpenAPI / JSON Schema 定义可靠地转换为 Effect Schema。背景JSON Schema 组合关键字的现实复杂性JSON SchemaDraft 2020-12 等允许在一个 schema 节点中同时使用多种约束关键字。例如下面这个 schema 合法且常见{ type: [string, number], anyOf: [ { pattern: ^[a-z]$ }, { minimum: 0 } ] }这里type与anyOf互为“兄弟关键字”sibling keywords语义上要求同时满足type的约束和anyOf中任意一个分支的约束即二者取交集。类似地oneOf要求恰好命中一个分支allOf要求全部满足而const/enum/$ref也可能与它们叠写在同一节点上。许多简化型导入器会直接忽略这类“兄弟关键字组合”或仅处理顶层是纯anyOf/oneOf的情况导致真实世界中的 API 定义无法正确导入。本次 changeset.changeset/pre/fix-json-schema-anyof-oneof-siblings.md宣告的修复正是针对这一缺口SchemaRepresentation: supportanyOf/oneOfwith sibling keywords infromJsonSchemaMultiDocument多文档导入模型fromJsonSchemaMultiDocument是什么在 SchemaRepresentation.ts 中fromJsonSchemaMultiDocument被定义为多文档多根导入入口与单文档入口fromJsonSchema互补。它的核心能力是一次导入一个包含多个根 schema的文档并共享一份$defs定义表根 schema 之间保持顺序且重复引用的定义只被解析一次支持$ref引用、递归定义、别名链解析A - B - C的连续$ref通过options如patterns、onEnter、onLeave控制导入策略。其测试fromJsonSchemaMultiDocument.test.ts覆盖了 pattern 策略传播、onEnter异常保持、contentSchema注解透传、不可达定义跳过、根顺序保持与定义共享、别名链、独立递归定义、缺失引用与循环别名报错等大量行为。一个典型的调用形态如下来自测试文件import { Schema, type SchemaAST, SchemaRepresentation } from effect const schemas SchemaRepresentation.fromJsonSchemaMultiDocument({ dialect: draft-2020-12, schemas: [{ $ref: #/$defs/A }, { $ref: #/$defs/A, description: second }], definitions: { A: { type: string, minLength: 1 } } }) // schemas[0].ast / schemas[1].ast 即为可用的 Schema AST实现原理recur中的组合顺序本次修复的核心位于 fromJsonSchemaDocument.ts。在递归处理recur内部先是解析基础表示type关键字分派到Null/String/Number/Boolean/Objects/Arrays等具体表示随后按固定顺序叠加“修饰性关键字”constL899-L905字面量通过makeJsonLiteral转为Literal表示并用combine与当前表示求交enumL906-L915多个枚举值构造成mode: anyOf的Union再与当前表示求交$refL916-L937解析引用键若当前表示仍为Unknown则直接使用Reference否则解析引用并与当前表示combineallOfL944-L952逐项recur后与累计表示求交anyOf/oneOfL954-L965这是本次增强的关键——将分支成员逐个recur构造出携带modeanyOf | oneOf与checks的Union表示然后执行combine(union, representation, path)把兄弟关键字产生的交集语义落实到表示层。for (const mode of [anyOf, oneOf] as const) { const members schema[mode] if (Array.isArray(members)) { const union: ImportedJsonSchemaRepresentation { _tag: Union, types: members.map((member, index) recur(member, [...path, mode, index])), mode, checks: [] } representation combine(union, representation, [...path, mode]) } }注意types中每个分支都会recur这意味着分支内部依然可以递归出现anyOf/oneOf/allOf/$ref等嵌套结构同时mode被保留在Union表示上参见 SchemaRepresentation.ts 中的mode: anyOf | oneOf让下游可以区分“任意命中”与“恰好命中”两种组合语义。combine表示层上的求交引擎兄弟关键字组合之所以能正确工作依赖的是 combine 这个表示层求交函数。从源码结构可以梳理出它的分派逻辑Unknown与任何表示求交结果是对方本身恒等元素Reference先resolveReference解析再继续求交Suspend先解开thunk再求交Union 与 Union走combineUnions其中combineLiteralUnions会专门处理两个mode均为anyOf的字面量联合合并L632-L651并借助isTypePartition/combinePartition对按根类型划分的联合进行分治合并L704-L735Union 与普通类型走combineUnionWithType把type约束“分发”进联合的每个成员L656-L687同类型表示String/Number/Boolean/Arrays/Objects之间合并各自的checks必要时合并注解与构造器信息。例如{ type: string, anyOf: [{ minLength: 2 }, { pattern: ^a }] }会被解析为String表示与一个anyOf模式的Union求交字符串的minLength等checks通过collectStringChecks收集见 L989-L992与联合分支的过滤条件会被正确组合最终产出一个既限制类型又限制取值空间的 Schema。combineUnions中的一段关键逻辑是combineUnionWithType当Union与一个具体类型求交时会按成员下标将类型逐个合并进成员function combineUnionWithType( union: ImportedJsonSchemaRepresentation, type: ImportedJsonSchemaRepresentation, path: Path, partitionOnLeft: boolean ): ImportedJsonSchemaRepresentation { /* ... */ }递归定义上的兄弟关键字明确的边界源码对“递归引用 兄弟关键字”这一棘手场景给出了明确态度。在$ref处理分支L930-L936中若引用是递归的且节点上还带有需要解析的断言兄弟关键字会抛出Unsupported assertion siblings on recursive reference ${reference.$ref}也就是说对普通引用兄弟关键字可以与$ref求交组合但对递归引用自身直接或间接引用自己在同一节点叠加其它断言会破坏不动点结构导入器选择显式报错而非静默吞掉语义。这一设计可以在编写递归 Schema 时作为参考把额外约束放到递归引用的外层包装节点而不是与递归$ref叠写在同一层。同样地if/then/else、not、dependentSchemas、unevaluatedProperties等关键字目前仍属不支持范围会在 L886-L894 抛出Unsupported JSON Schema keyword错误。测试验证与使用建议fromJsonSchemaMultiDocument.test.ts 中的测试importAndLower辅助函数先将文档导入为 Schema AST再降级为MultiDocument表示覆盖了定义共享、引用解析、递归跟踪、错误路径等行为是理解导入器契约的可靠参考。实际使用fromJsonSchemaMultiDocument时的建议优先通过options.patterns显式声明pattern关键字的处理策略error|apply默认策略会在遇到pattern时直接抛错避免静默丢失约束利用onEnter/onLeave钩子做自定义校验或转换测试证实onEnter抛出的原始异常对象会被原样透传保留引用同一性便于错误处理同一文档内尽量复用$defs引用导入器会按根顺序保留结构并共享定义解析结果避免重复展开带来的膨胀若需要把导入结果再序列化为 JSON Schema可配合toJsonMultiDocument/toRepresentations使用二者在测试中互相印证。小结本次 changeset 让fromJsonSchemaMultiDocument对“组合关键字 兄弟关键字叠写”的 JSON Schema 具备了完整支持anyOf/oneOf会先被构造成携带mode的Union表示再通过表示层的combine求交引擎与type、const、enum、allOf、$ref等兄弟约束组合。对于递归引用上的断言兄弟关键字导入器选择显式报错以保证语义正确。这意味着现实世界中大量“既限定类型、又提供多分支约束”的 API 契约OpenAPI、MCP 工具 Schema 等都可以被可靠地转换为 Effect Schema是 Schema 互操作链路中一次扎实的能力补齐。【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表