【问题标题】:How to properly pattern match JsonConversions如何正确地模式匹配 JsonConversions
【发布时间】:2020-05-31 12:31:56
【问题描述】:

您好,我有以下代码可以按预期工作,但是当我在 Option.defaultWith 函数中进行模式匹配时,编译器会警告我模式匹配不完整。有没有更聪明的方法来达到同样的效果但没有警告?

我一直在考虑为其余情况抛出异常,但这很丑。

namespace JsonParser

open System
open System.Globalization
open FSharp.Data
open FSharp.Data.Runtime

type public Key = string

type public Value =
    | Int of int
    | Double of double
    | Decimal of decimal
    | String of string
    | DateTime of DateTime
    | Boolean of Boolean
    | Array of Value []
    | Guid of Guid
    | Null
    | Object of Record []

and public Record =
    { Key: Key
      Value: Value }

module public Json =

    let private culture = CultureInfo.InvariantCulture
    let private emptyArray = Array.empty<String>

    let rec private map (value: JsonValue) =
        JsonConversions.AsInteger culture value
        |> Option.map Value.Int
        |> Option.orElseWith (fun () -> JsonConversions.AsDecimal culture value |> Option.map Value.Decimal)
        |> Option.orElseWith (fun () -> JsonConversions.AsFloat emptyArray true culture value |> Option.map Decimal |> Option.map Value.Decimal)
        |> Option.orElseWith (fun () -> JsonConversions.AsGuid value |> Option.map Value.Guid)
        |> Option.orElseWith (fun () -> JsonConversions.AsDateTime culture value |> Option.map Value.DateTime)
        |> Option.orElseWith (fun () -> JsonConversions.AsBoolean value |> Option.map Value.Boolean)
        |> Option.defaultWith (fun () ->
            match value with
            | JsonValue.String x -> Value.String x
            | JsonValue.Null -> Value.Null
            | JsonValue.Array x ->
                x
                |> Array.map map
                |> Value.Array
            | JsonValue.Record x ->
                x
                |> Array.map (fun (x, y) ->
                    { Key = x
                      Value = map y })
                |> Value.Object)

【问题讨论】:

  • 你为什么不只匹配 JsonValue.Null (等),在字符串的情况下,你可以尝试解析为 DateTime、Guid 等。你的代码会(IMO)更具可读性.
  • 是的,这是个好主意。我今天实施了类似的方法。但是,充分利用 JsonValue 的最佳做法是什么?
  • 我认为 JsonValue 可区分联合的全部意义在于,您可以将解析后的值与它进行匹配以确定它的真正含义。你已经在做,但是在你运行完所有那些 Option.orElseWith 语句之后。直接匹配就行了。这有帮助吗?
  • 但对于我想要解析的情况,例如 GuidDateTime 。它需要我将JsonValue.String 传递给一个额外的解析函数。在我的理想情况下,我希望在包含这些情况的情况下进行简单的模式匹配。

标签: f# f#-data


【解决方案1】:

答案实际上取决于您希望如何处理 JSON 数据中的各种极端情况。

JsonConversions 中的操作的实现方式是,只要可以合理地完成,它们就会尝试将值转换为目标类型。这意味着使用这些值,true1"yes" 将全部转换为布尔值true。这是你想要的吗?如果是这样,那么我可能只是在引发异常的模式匹配中添加一个案例,说这种情况不应该发生:

match value with
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x -> (...)
| JsonValue.Record x -> (...)
| JsonValue.Float _ | JsonValue.Number _ | JsonValue.Boolean _ -> 
    failwith "should never happen: Numbers and booleans handled earlier!"    

如果您想将 JSON 值 "yes" 转换为 Value.String("yes") 而不是 Value.Boolean(true),那么直接在 JsonValue 上进行模式匹配会容易得多:

let rec private map (value: JsonValue) =
    match value with
    | JsonValue.Float f -> Value.Double f
    | JsonValue.Number n -> Value.Decimal n 
    | JsonValue.Boolean b -> Value.Boolean b
    | JsonValue.String x -> Value.String x
    | JsonValue.Null -> Value.Null
    | JsonValue.Array x ->
        x |> Array.map map |> Value.Array
    | JsonValue.Record x ->
        x |> Array.map (fun (x, y) -> { Key = x; Value = map y }) |> Value.Object

您可以通过查看源代码中的相关文件:JsonConversionsTextConversions,找到有关 JsonConversions 工作原理的详细信息。

【讨论】:

  • 啊,我明白了,在我最初的实现中,我从JsonValue 的简单模式匹配开始,但我希望能够映射到GuidDateTime。当我得到字符串时,这让我做了Guid.TryParse 等,但我认为JsonConversions 将是解决方案,而不是使用我的函数将它们粘合在一起,它最终得到了我的示例代码。
  • 感谢您的意见!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-15
  • 1970-01-01
  • 2010-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多