如何正确模式匹配 JsonConversions

How to properly pattern match JsonConversions

您好,我有以下代码,它按预期工作,但是当我在 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)

答案实际上取决于您希望如何处理 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),那么直接在[=上进行模式匹配就容易多了20=]:

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

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