无法序列化F#Chiron中的Discriminated Union

时间:2016-05-18 14:55:40

标签: serialization f# discriminated-union

如果我有:

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

3 个答案:

答案 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"您可以使用truefalse来摆脱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为受歧视的工会定义ToStringFromString函数:

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,我无法帮助你。但这可能会给你一个起点。