如何使用 f# 反序列化 json 以获得正确的类型

How to deserialize json for correct type using f#

我是 F# 和函数式编程的新手。

我有这两种

type User= { 
    active: bool 
    funds: int 
}  
and UserDto = {
    user: User
}

type Transaction = {
    amount: int
    time: DateTime 
}
and TransactionDto = {
    transaction: Transaction
}

而且我必须将一些 JSON 字符串作为输入,它可以是 UserTransaction

我正在尝试使用 Fsharp.Json,如果我知道类型,它就可以正常工作。

let getUserInput = 
    let input = System.Console.ReadLine()
    let acc = Json.deserialize<Account> input
    printfn "%A" acc

有没有办法在不验证输入字符串是否包含 account 的情况下执行此序列化,如果输入字符串包含 transaction 则执行其他操作?

[编辑]

正如你们所说,我确实喜欢 Devon Burriss 所说的,但使用了不同的方法。 它遵循函数

let getOperation json =
    let j = JObject.Parse json
    match j.Properties().Select(fun p -> p.Name).FirstOrDefault() with
        | "account" -> AccountOperation(JsonConvert.DeserializeObject<AccountDto>(json))
        | "transaction" -> TransactionOperation(JsonConvert.DeserializeObject<TransactionDto>(json))
        | _ -> InvalidOperation "invalid-operation"

如前所述,您需要知道要反序列化为该类型的类型。

如果由于某种原因您不知道传入的类型并且无法控制客户端,我会发送额外的信息。一个类型名称,或者因为您使用的是 F#,所以序列化一个总和类型,如下所示:

type Dto =
| UserType of User
| UserDtoType of UserDto
| TransactionType of Transaction
| TransactionDtoType of TransactionDto

现在您可以反序列化为 Dto 并从那里进行模式匹配。

match v with
| UserType x -> printfn "I am a User %A" x
| UserDtoType x -> printfn "I am a UserDto %A" x
| TransactionType x -> printfn "I am a Transaction %A" x
| TransactionDtoType x -> printfn "I am a TransactionDto %A" x

I know you specifically mentioned NOT checking the properties but I did want to point out going to a sum type if you do go that route.

如果您不能更改客户端以发送更多信息,您可以检查 JSON 并在反序列化时转到 sum 类型,但这是非常手动且容易出错的,但我没有看到另一个方法。当然,你可以让它更漂亮,但它应该能证明我的意思。

let serialize x = JsonConvert.SerializeObject(x)
let deserialize json = 
    let jObj = JObject.Parse(json)
    if(jObj.ContainsKey("active")) then Dto.UserType (jObj.ToObject<User>())
    elif(jObj.ContainsKey("user")) then Dto.UserDtoType (jObj.ToObject<UserDto>())
    elif(jObj.ContainsKey("amount")) then Dto.TransactionType (jObj.ToObject<Transaction>())
    elif(jObj.ContainsKey("transaction")) then Dto.TransactionDtoType (jObj.ToObject<TransactionDto>())
    else failwith "Unknown type"

Note: This is using Newtonsoft purely because I know it has the lower level JObject and am not familiar with any lower level FSharp.Json features.