用途
该技能用于编写或审查涉及 F# 类型序列化的代码,目标是让 F# 记录、联合类型(union)、option 与 Map 序列化出的 JSON 与既定线上契约(wire contract)一致,并保证旧版本 JSON 仍能被正确读取。
主要能力与关键规则
资料指出的核心问题:System.Text.Json 是为 C# 类设计的。处理 F# 类型时,它会对联合类型抛出 NotSupportedException,把缺失的记录字段填为 null 或 0,把 JSON 的 null 读作 null 记录。FSharp.SystemTextJson 可以修复这些异常,但其默认输出结构(如 {"Case":...,"Fields":[...]})往往不是其他服务使用的形状;序列化器自身能往返,不代表与其他服务的每条消息都一致。
技能给出的规则包括:
- 先写线上契约,再选设置。为每个类型记录属性名、联合形状、可选值缺失或 null 的含义、Map 键形状、必填字段、未知属性与未知分支的处理方式。
- 不要单独用 System.Text.Json 处理 F# 领域类型;不要对不归自己掌控的契约沿用 FSharp.SystemTextJson 默认值。
- 逐个设置联合类型编码方式、标签名与标签命名策略;PropertyNamingPolicy 不影响联合标签。
- 编码标志作用于所有联合类型;个别联合需要不同形状时用 WithOverrides;类型上的 [<JsonFSharpConverter>] 在存在全局选项时不生效,除非调用 WithAllowOverride。
- 要省略 None 并把缺失或 null 读作 None,用 WithSkippableOptionFields(SkippableOptionFields.Always, deserializeNullAsNone = true);无参 WithSkippableOptionFields() 会把 string option 中的 null 读成 Some null。不要用 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull 达到此目的,它会导致缺失必填字段被读成 null 并抛 NullReferenceException 等问题。
- 包裹 option 的单分支联合(如 Box of int option)会把 Box None 写成 null 并读回 null 联合;option 的 option 会把 Some None 写成 null。需改类型或写转换器。
- 解码边界上,JsonException 应返回 Error;若用 System.Text.Json 直接读取、读入 DTO 类或读入包裹 option 的单分支联合,null 结果也应返回 Error。
- 线上形状与领域类型不一致或领域类型构造函数私有时,先解码到 DTO 或用 Thoth.Json 解码器,再解析为领域类型。
- Native AOT 与裁剪场景参考 fsharp-native-aot;本技能不覆盖源生成。
使用方式
资料给出的步骤:把线上契约写成列表或表格;获取对方写出或改动前代码写出的 JSON 样本,放在测试旁边;选择 FSharp.SystemTextJson 设置、Thoth.Json 编码器或 DTO 层,每个契约只建一个 options 值并在该契约的所有调用中使用;对每个保存的样本验证解码到预期 F# 值、预期值又编码回相同 JSON(比较解析后的 JSON 而非文本);为未被样本覆盖的契约规则补充用例,包括可选值缺失、可选值为 null、必填字段缺失、必填字段为 null、未知属性、未知联合分支、JSON null 与畸形 JSON;确认输出不含契约未使用的 Case 或 Fields 属性,且解码函数返回 Error 而不抛异常。
验证方式为在技能根目录运行:
pwsh ./scripts/run.ps1
该脚本使用 FSharp.SystemTextJson 1.4.36 与 Thoth.Json.System.Text.Json 0.5.0 在 .NET 10 上展示各项默认值与设置,结果与预期不符时失败。参考资料包括 references/SETTINGS.md(System.Text.Json 默认值、各联合编码输出、option/Map/命名设置)与 references/WIRE-COMPATIBILITY.md(增删改联合分支或记录字段前阅读,含样本测试、Thoth.Json 与 DTO 层)。
适用场景
适用于 F# 服务间通信、需要与外部服务约定的 JSON 接口、需要兼容历史 JSON 数据的场景,以及审查 F# 序列化代码和配置的工作。
限制
资料未说明该技能的安装方式、收费模式、运行平台范围(仅提及 .NET 10 用于验证脚本)或具体版本以外的兼容性;不覆盖源生成,AOT 与裁剪另见其他技能。