F#与查询结果匹配。有没有一种优雅的方法可以做到这一点?
通过解析json,我得到了JObject类型的结果: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
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❤)