如何使用Thoth.Json.Net根据type属性对JSON数组对象做条件解析
Thoth.Json.Net 基于
type字段选择解码器实现方案 核心实现思路
你可以通过Decode.field先取出type字段的值,再用Decode.andThen根据值匹配对应的解码器,最终所有解码器返回同一个可辨识联合类型即可统一处理数组元素。
基础实现步骤
- 首先定义对应不同JSON对象类型的可辨识联合,例如:
type MyJsonElement = | TypeA of TypeAData | TypeB of TypeBData | TypeC of TypeCData
- 为每个子类型实现对应的解码器:
let typeADecoder = Decode.map TypeA <| Decode.object (fun get -> // 按TypeA的结构读取字段 { Id = get.Required.Field "id" Decode.int Name = get.Required.Field "name" Decode.string }) let typeBDecoder = Decode.map TypeB <| Decode.object (fun get -> // 按TypeB的结构读取字段 { Id = get.Required.Field "id" Decode.int Value = get.Required.Field "value" Decode.float })
- 实现按
type字段匹配的统一解码器:
let elementDecoder = Decode.field "type" Decode.string |> Decode.andThen (function | "typeA" -> typeADecoder | "typeB" -> typeBDecoder | other -> Decode.fail $"未知的元素类型: {other}")
- 解析数组时直接使用该统一解码器即可:
let rootDecoder = Decode.field "arrayField" (Decode.list elementDecoder)
提问者更新的优化方案
我在最初的临时方案跑通之后,重新尝试使用计算表达式(CE)和decodeByType自定义解码器,按照@tranquillity提到的方案让每个解码器返回一个可辨识联合类型的值。理清所有类型关系后,我仅需要为Builder指定类型即可,代码如下:
type DecoderBuilder () = member __.Bind((decoder:Decoder<'a>), (func:('a -> Decoder<'b>))) = Decode.andThen func decoder
这样就可以像@brianberns描述的那样,使用CE轻松组合多个解码器。我为自定义解码器和可辨识联合新建了单独的文件,实现了更贴合领域模型的JSON值提取逻辑——如果JSON结构无效就会直接返回错误,从根本上避免了不可表示状态的出现。
整体代码更简洁,函数式风格更显著,也更优雅,感谢大家的帮助。
内容的提问来源于stack exchange,提问作者Francis Judge
相关产品推荐
相关产品推荐

