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

时间:2019-11-06 15:08:31

标签: 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"] <> null then
    ... do stuff

有没有一种干净的方法来进行比赛?

做类似

的语句
| _ when j.["error"] <> null ->

看起来不是很干净。可以做得更好吗?

4 个答案:

答案 0 :(得分:2)

  1. 要对第一个非空值进行处理:
    let j = JObject.Parse x
    let doSomething s = printf "%A" s
    if isNull j then
        ()
    else
        [ j.["aa"]; j.["bb"]; j.["cc"] ]
        |> List.tryFind (fun s -> s |> Option.ofObj |> Option.isSome)
        |> doSomething
  1. 或者对每个非null值执行操作:
    let j = JObject.Parse x
    let doSomething s = printf "%A" s
    if isNull j then
        ()
    else
        [ j.["aa"]; j.["bb"]; j.["cc"] ]
        |> List.choose (fun s -> s |> Option.ofObj)
        |> List.iter doSomething
  1. 或者对第一个非空值执行不同的操作(取决于哪个值非空):
    let j = JObject.Parse x
    let doSomethingA s = printf "%A" s
    let doSomethingB s = printf "%A" s
    let doSomethingC s = printf "%A" s
    if isNull j then
        ()
    else
        [ 
            j.["aa"], doSomethingA
            j.["bb"], doSomethingB
            j.["cc"], doSomethingC
        ]
        |> List.tryFind (fun (s, _) -> s |> Option.ofObj |> Option.isSome)
        |> Option.iter (fun (s, f) -> f s)

答案 1 :(得分:2)

如果您创建一个活动模式,该模式返回匹配的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)

...解析函数...

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❤)

答案 2 :(得分:1)

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

let (|NonNull|_|) = function null -> None | v -> Some v

...这将允许以下操作。

if isNull j then
    //do stuff
else
    match j.["aa"], j.["bb"], j.["cc"], j.["dd"] with
    | NonNull aa, _, _, _ -> //do stuff
    | _, NonNull bb, _, _ -> //do stuff
    | _, _, NonNull cc, NonNull dd -> //do stuff

答案 3 :(得分:1)

您可以为每个键列出一个动作列表,以便可以对每个键统一应用null检查逻辑。

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的情况下,可以使用select来仅获取有效的值,操作。

  ["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的结果。