F#与查询结果匹配。有没有一种优雅的方法可以做到这一点?

F#与查询结果匹配。有没有一种优雅的方法可以做到这一点?,f#,F#,通过解析json,我得到了JObject类型的结果: let j = JObject.Parse x 我必须执行的代码如下: if j = null then ... do stuff else if j["aa"] <> null then ... do stuff else if j["bb"] <> null then ... do stuff else if j["cc"] <> null and j["dd"] <&g

通过解析json,我得到了JObject类型的结果:

let j = JObject.Parse x
我必须执行的代码如下:

if j = null then
    ... do stuff
else if j["aa"] <> null then
    ... do stuff
else if j["bb"] <> null then
    ... do stuff
else if j["cc"] <> null and j["dd"] <> null then
    ... do stuff
如果j=null,则
... 做事
否则,如果j[“aa”]为空,则
... 做事
否则,如果j[“bb”]为空,则
... 做事
否则,如果j[“cc”]为空且j[“dd”]为空,则
... 做事
这场比赛有没有干净的方法

做这样的陈述

| _ when j.["error"] <> null ->
| u;当j.[错误]为空->

看起来不是很干净。这能做得更好吗?

您可以创建一个活动模式来匹配非空值

let(| NonNull | | |)=函数null->None | v->Some v
…这将允许以下操作

如果为空,则
//做事
其他的
将j.[“aa”]、j.[“bb”]、j.[“cc”]、j.[“dd”]与
|非空aa,,,,->//执行操作
|_u,非空bb,u,->//做一些事情
|_u,u,非空cc,非空dd->//做一些事情

您可以为每个键创建一个操作列表,以便为每个键统一应用空检查逻辑

let j = JObject.Parse x
let doStuff key value = printfn "%s=>%s" key value
如果您想对每个键应用doStuff,您可以进行迭代。这是您的示例,但没有其他示例,因此它适用于每个关键点

  ["aa", doStuff
   "bb", doStuff
   "cc", doStuff]
  |> List.iter (fun (key,action) -> 
    j.TryGetValue key
    |> snd
    |> Option.ofObj
    |> Option.iter (action key))
如果只对第一个键使用doStuff,则可以使用choose更紧密地匹配您的示例,从而仅获取有效值actions

  ["aa", doStuff
   "bb", doStuff
   "cc", doStuff]
   |> Seq.choose (fun (key,action) ->
      j.TryGetValue key
      |> snd
      |> Option.ofObj
      |> Option.map (fun v -> action key v))
   |> Seq.tryHead
如果存在匹配的键且doStuff返回值,则此版本还返回应用的doStuff的结果。这有点滥用了Seq的懒惰特性,只调用第一个值,但也可以将Seq.tryHead的结果映射到函数调用

  • 要对第一个非空值执行操作,请执行以下操作:
  • 或者对每个非空值执行以下操作:
  • 或者对第一个非空值执行不同的操作(取决于哪个值是非空的):

  • 如果创建一个活动模式,该模式返回匹配的
    JToken

    let (|NonNull|_|) prop (o : JObject) =
        o.[prop] |> Option.ofObj
    
    你可以这样写:

    let handleAA (a : JToken) = ()
    
    match JObject.Parse "{}" with
    | null -> () // ...
    | NonNull "aa" a -> handleAA a
    | NonNull "bb" b & NonNull "cc" c -> ()
    | _ -> () // all other
    
    更新

    如果你需要更多的能量,活动模式会更多

    let (|J|_|) prop (o : obj) =
        match o with
        | :? JObject as o -> o.[prop] |> Option.ofObj
        | _ -> None
    
    let (|Deep|_|) (path : string) (o : obj) =
        let get t p = t |> Option.bind (fun t -> (``|J|_|``) p t)
        match o with
        | :? JToken as t ->
            path.Split('.') |> Array.fold get (Option.ofObj t)
        | _ -> None
    
    。。。一些助手

    let jV (t : JToken) = t.Value<string>()
    let handle t = jV t |> printfn "single: %s"
    let handle2 a b = printfn "(%s, %s)" (jV a) (jV b)
    
    。。。然后我们开始:

    parse "null" // null
    parse "42" // val: 42L
    parse "{ aa: 3.141 }" // single: 3.141
    parse "{ bb: 2.718, cc: \"e\" }" // (2.718, e)
    parse "{ bb: 2.718, dd: 0 }" // single: 2.718
    parse "{ foo: { bar: \"baz\" } }" // single: baz
    parse "{ foo: { bar: \"baz\" }, hello: { world: \"F#|>I❤\" } }" // (baz, F#|>I❤)
    

    当你说“干净”和“更好”时,你的意思是什么?(记住,基于观点的问题在堆栈溢出问题上是离题的)我怀疑它们的意思是“更惯用”@VoronoiPotato说对了:)这可能会很快变得非常大;虽然这个想法很好,但我担心在使用了几个令牌之后,它不会提高可读性。我担心这会使您需要检查多个组合的场景变得复杂:同时检查这个令牌和那个令牌,etcI认为在所有答案中,这将最有效;这只是语法上的甜点,但有助于可读性。再次查看后,它将不起作用,因为它不允许链:[“token1”][“token2”]您可以测试一个或另一个标记,但不能测试例如token2是否存在(只有在token1存在时才能存在)。@Thomas请查看我更新的答案。由您决定这是否仍然是
    干净的
    ;-)@托马斯第一版现在展示了如何组合案例。还有另一个更新:“强大”版本现在简单多了。
    let (|J|_|) prop (o : obj) =
        match o with
        | :? JObject as o -> o.[prop] |> Option.ofObj
        | _ -> None
    
    let (|Deep|_|) (path : string) (o : obj) =
        let get t p = t |> Option.bind (fun t -> (``|J|_|``) p t)
        match o with
        | :? JToken as t ->
            path.Split('.') |> Array.fold get (Option.ofObj t)
        | _ -> None
    
    let jV (t : JToken) = t.Value<string>()
    let handle t = jV t |> printfn "single: %s"
    let handle2 a b = printfn "(%s, %s)" (jV a) (jV b)
    
    let parse o =
        match JsonConvert.DeserializeObject o with
        | null -> printfn "null"
        | J "aa" a -> handle a
        | J "bb" b & J "cc" c -> handle2 b c
        | J "bb" b & J "dd"  _ -> handle b
        | Deep "foo.bar" bar & Deep "hello.world" world -> handle2 bar world
        | Deep "foo.bar" bar -> handle bar
        | o -> printfn "val: %A" o
    
    parse "null" // null
    parse "42" // val: 42L
    parse "{ aa: 3.141 }" // single: 3.141
    parse "{ bb: 2.718, cc: \"e\" }" // (2.718, e)
    parse "{ bb: 2.718, dd: 0 }" // single: 2.718
    parse "{ foo: { bar: \"baz\" } }" // single: baz
    parse "{ foo: { bar: \"baz\" }, hello: { world: \"F#|>I❤\" } }" // (baz, F#|>I❤)