Невозможно сериализовать Дискриминационный Союз в F# Chiron
Если у меня есть:
type a = B | C
Как мне написать статические члены ToJson и FromJson?
Я знаю, как написать его для типа записи (который показан в примерах на Chiron: JSON + Ducks + Monads), но я не могу найти никаких примеров для DU.
РЕДАКТИРОВАТЬ
После s952163 полезного ответа (и последующего комментария) я адаптировал код, чтобы попытаться работать с "простым" DU выбора A | B (а не A из строки | B из...). Мой код сейчас:
type SimpleDU =
| A
| B
static member ToJson (t : SimpleDU) =
match t with
| A -> Json.writeNone "a"
| B -> Json.writeNone "b"
static member FromJson (_ : SimpleDU) =
json {
let! duA = Json.tryRead "a"
match duA with
| Some s -> return s
| None -> return SimpleDU.B
}
Это компилируется, но когда я пытаюсь сделать это с примером кода операции:
let a = A
let b = B
let a2json = a |> Json.serialize
let (json2a:SimpleDU) = a2json |> Json.deserialize
let b2json = b |> Json.serialize
let (json2b:SimpleDU) = b2json |> Json.deserialize
json2a неправильно возвращает SimpleDU.B
3 ответа
Реализация, которая сериализует A
в Object (map [("SimpleDU", String "a")])
вместо Object (map [("a", Null null)])
является:
#I @"..\packages\Chiron.6.1.0\lib\net40"
#I @"..\packages\Aether.8.1.2\lib\net35"
#r "Chiron.dll"
#r "Aether.dll"
open Chiron
type SimpleDU =
| A
| B
static member ToJson x =
Json.write "SimpleDU" <|
match x with
| A -> "a"
| B -> "b"
static member FromJson(_ : SimpleDU) =
json {
let! du = Json.tryRead "SimpleDU"
match du with
| Some "a" -> return A
| Some "b" -> return B
| Some x -> return! Json.error <| sprintf "%s is not a SimpleDU case" x
| _ -> return! Json.error "Not a SimpleDU JSON"
}
// val serializedA : Json = Object (map [("SimpleDU", String "a")])
let serializedA = A |> Json.serialize
let serializedB = B |> Json.serialize
let (a : SimpleDU) = serializedA |> Json.deserialize
let (b : SimpleDU) = serializedB |> Json.deserialize
let aMatches = a = A
let bMatches = b = B
let serializedABBAA = [ A; B; B; A; A ] |> Json.serialize
let (abbaa : SimpleDU list) = serializedABBAA |> Json.deserialize
let abbaaMatches = abbaa = [ A; B; B; A; A ]
// allFine = true
let allFine = aMatches && bMatches && abbaaMatches
let defects =
Array [ Object <| Map.ofList [ ("SimpleDU", String "c") ]
Object <| Map.ofList [ ("Foo", String "bar") ] ]
// attempt = Choice2Of2 "Not a SimpleDU JSON"
let (attempt : Choice<SimpleDU list, string>) = defects |> Json.tryDeserialize
Вместо "a"
а также "b"
вы могли бы использовать true
а также false
который бы избавился от Some x
случае, но я бы предпочел читаемые случаи в JSON.
Вы также можете добавить статические члены в DU. В Chiron Taming Types в последнем параграфе есть ссылка, в которой говорится, что некоторые примеры с DU должны скоро появиться. Однако при условии, что вы не можете ждать, и что вы предпочитаете Chiron, а не Json.NET или FsPickler, вот пример. Возможно, есть и другие способы, но я не знаком с операторами Chiron, поэтому я решил использовать вычислительное выражение (взято из Chiron Computation Expressions). Идея в том, что вы можете соответствовать шаблону. Так что, вероятно, вы можете сопоставить паттерны и с более сложными DU. Если вы знакомы с Chiron, я уверен, что это можно сделать более идиоматичным. Вы можете видеть, что сам Chiron использует DU, и, например, объект Json - это map.
#I @"..\packages\Chiron.6.1.0\lib\net40"
#I @"..\packages\Aether.8.0.2\lib\net35"
#I @"..\packages\FParsec.1.0.1\lib\net40-client"
#r "Chiron.dll"
#r "Aether.dll"
#r "Fparsec.dll"
open Aether
open Chiron
open Chiron.Operators
open FParsec
type SimpleDU =
|A of string
|B of int * bool
static member ToJson (x: SimpleDU) =
match x with
| A s -> Json.write "A" s
| B (i, b) -> Json.write "B" (i, b)
static member FromJson (_ : SimpleDU) =
json {
let! duA = Json.tryRead "A"
match duA with
| Some s -> return A s
| None ->
let! x = Json.read "B"
return B x
}
И вот как это работает:
let a = A "Jason"
let b = B (13,true)
let a2json = a |> Json.serialize //val Json = Object (map [("A", String "Jason")])
let (json2a:SimpleDU) = a2json |> Json.deserialize //val json2a : SimpleDU = A "Jason"
let b2json = b |> Json.serialize
let (json2b:SimpleDU) = b2json |> Json.deserialize
Есть также несколько примеров в исходном коде, которые могут быть полезны для вас: Chiron
Мне кажется, что /questions/5433557/sozdat-diskriminirovannoe-obedinennoe-delo-iz-stroki/5433565#5433565 может вам помочь. Этот ответ указывает на этот фрагмент кода F#, определяющий ToString
а также FromString
функции для дискриминируемых союзов:
open Microsoft.FSharp.Reflection
let toString (x:'a) =
match FSharpValue.GetUnionFields(x, typeof<'a>) with
| case, _ -> case.Name
let fromString<'a> (s:string) =
match FSharpType.GetUnionCases typeof<'a> |> Array.filter (fun case -> case.Name = s) with
|[|case|] -> Some(FSharpValue.MakeUnion(case,[||]) :?> 'a)
|_ -> None
Вам все равно нужно будет перейти от строки (просто "A" или "B") к полному объекту DU (например, прочитать остальные данные DU в s952163). SimpleDU
пример), и поскольку я еще не использовал Chiron, я не могу вам чем-то помочь. Но это может дать вам отправную точку.