如何使用 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 字符串作为输入,它可以是 User
或 Transaction
我正在尝试使用 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.
我是 F# 和函数式编程的新手。
我有这两种
type User= {
active: bool
funds: int
}
and UserDto = {
user: User
}
type Transaction = {
amount: int
time: DateTime
}
and TransactionDto = {
transaction: Transaction
}
而且我必须将一些 JSON 字符串作为输入,它可以是 User
或 Transaction
我正在尝试使用 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.