Как десериализовать JSON для правильного типа, используя 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 делать другой?

[РЕДАКТИРОВАТЬ]

Как вы, ребята, сказали, я, как сказал Девон Буррисс, но использовал другой подход. Следует за функцией

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"

1 ответ

Решение

Как уже указывалось, вам нужно знать тип для десериализации этого типа.

Если по какой-то причине вы не знаете тип входящего и не можете контролировать клиента, я отправлю дополнительную информацию. Имя типа или поскольку вы используете 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

Я знаю, что вы упомянули НЕ проверять свойства, но я хотел указать на тип суммы, если вы пойдете по этому пути.

Если вы не можете изменить клиента для отправки дополнительной информации, вы можете проверить JSON и перейти к типу суммы при десериализации, но это вполне вручную и подвержено ошибкам, но я не вижу другого пути. Можно, конечно, сделать это красивее, но это должно продемонстрировать, что я имею в виду.

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"

Примечание: это использует Newtonsoft исключительно потому, что я знаю, что он имеет нижний уровень JObject и не знаком ни с каким нижним уровнем FSharp.Json функции.

Другие вопросы по тегам