如果我有:
type a = B | C
如何编写静态成员ToJson和FromJson?
我知道如何为记录类型编写它(在Chiron: JSON + Ducks + Monads的示例中显示)但我找不到DU的任何示例。
修改
按照s952163的有用答案(以及后续评论),我已经调整了代码以尝试使用“简单”选择的DU B(而不是......的字符串| B的A)。我的代码现在是:
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
答案 0 :(得分:4)
将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中的案例。
答案 1 :(得分:1)
您也可以将静态成员添加到DU。在最后一段的Chiron Taming Types中,有一个链接提到有关DU的一些示例应该很快就会出现。但是假设您不能等待并且您更喜欢Chiron而不是Json.NET或FsPickler这里是一个例子。可能还有其他一些方法,但我不熟悉Chiron的运算符,所以我决定使用计算表达式(从Chiron Computation Expressions窃取)。这个想法是你可以模式匹配。因此,您可以在更复杂的DU上进行模式匹配。如果您熟悉Chiron,我相信它可以更加惯用。您可以看到Chiron本身正在使用DUs,例如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
答案 2 :(得分:0)
我觉得https://stackoverflow.com/a/36828630/2314532可能会对您有所帮助。该答案指向this F# snippet为受歧视的工会定义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
你仍然需要从字符串(只是&#34; A&#34;或&#34; B&#34;)到完整的DU对象(例如,阅读其余的DU&#39; s952163&#39; SimpleDU
示例中的数据),由于我还没有使用过Chiron,我无法帮助你。但这可能会给你一个起点。