Thoth.Json.Net 条件解析

Thoth.Json.Net conditional parsing

我有一个 JSON 文档,我正在使用 Thoth.Json.Net 解析它。该文档有一个包含一组对象的数组,每个对象都有一个“类型”属性,其值标识它们的类型。这些类型中的每一种都需要不同的解码器,因此我需要能够根据“类型”属性的值提供某种过滤器。我该怎么做?

更新:

在我上面描述的“hack”工作后,我重新使用 CE 和 decodeByType 自定义解码器,每个解码器从上面提到的 @tranquillity 中的可区分联合返回一个值。一旦我了解了所有类型,我唯一要做的就是为 Builder 指定类型:

Builder:   type DecoderBuilder () = 
    member __.Bind((decoder:Decoder<'a>), (func:('a -> Decoder<'b>))) = 
      Decode.andThen func decoder

这使得使用 CE 可以轻松组合解码器,如下面的 @brianberns 所述。我为自定义解码器和 Discriminated Union 创建了一个新文件,发现我能够从 JSON 中提取更接近域模型的值(使无法表示的状态变得不可能,因为如果 JSON结构无效)。

总而言之,代码更简洁、更实用、更优雅。谢谢你的帮助。

我不是透特专家,但这就是我要做的。首先,我发现使用计算表达式组合解码器更容易:

type DecodeBuilder() =
    member _.Bind(decoder, f) : Decoder<_> =
        Decode.andThen f decoder
    member _.Return(value) =
        Decode.succeed value
    member _.ReturnFrom(decoder : Decoder<_>) =
        decoder

let decode = DecodeBuilder()

然后我发明了两个自定义解码器,只是作为例子,并按名称将它们放在一个地图中。一个反转字符串,一个使用 ROT13 密码解码。当然,您将在这里使用自己的自定义解码器:

let decodeReverse =
    decode {
        let! str = Decode.string
        return str
            |> Seq.rev
            |> Seq.toArray
            |> String
    }

let decodeRot13 =

    let rot13 c =
        if 'a' <= c && c <= 'm' || 'A' <= c && c <= 'M' then
            char (int c + 13)
        elif 'n' <= c && c <= 'z' || 'N' <= c && c <= 'Z' then
            char (int c - 13)
        else c
        
    decode {
        let! str = Decode.string
        return str
            |> Seq.map rot13
            |> Seq.toArray
            |> String
    }

let customDecoders =
    Map [
        "Reverse", decodeReverse
        "Rot13", decodeRot13
    ]

那么,自定义解码就是解码“type”字段,查找对应的自定义解码器,并用它来解码“value”字段:

let decodeByType =
    decode {
        let! typ = Decode.field "type" Decode.string
        return! Decode.field "value" customDecoders.[typ]
    }

用法示例:

Decode.fromString
    (Decode.array decodeByType)
    """
    [
        {
            "type" : "Reverse",
            "value" : "edcba"
        },
        {
            "type" : "Rot13",
            "value" : "qrpbqr guvf"
        }
    ]
    """
    |> printfn "%A"   // Ok [|"abcde"; "decode this"|]

我把完整的程序放上去here供参考