在哪里可以找到F#区分联合的序列化器?

时间:2010-09-15 17:43:13

标签: serialization f# discriminated-union

我需要将使用F#区分联合表示的抽象语法树保持为人类可读的紧凑格式,例如已经在F#语言中用于构造区别联合的格式,我可以稍后读回有区别的联合实例。我有点惊讶F#库不支持这一点,因为它肯定必须在编译器和F#交互中以有效的方式完成。

是否有任何免费/开源实施以合理(但不一定非常)高效的方式进行此操作?

注意:我不想要基于XML的序列化。

1 个答案:

答案 0 :(得分:5)

编辑:以下答案中的任何一个都不符合您的标准,但我发帖以防其他寻找联合序列化的人发现它们很有用。我不知道任何库方式重新解析联合上sprintf "%A"的输出 - 记住编译器和FSI有一个非常不同的任务,知道范围和处理命名空间和限定名称和阴影和什么,甚至忽略这一点,解析联合所携带的数据(整数,字符串,任意对象等)本身就是一项整体任务。

这是联合序列化的一种策略,作为小样本程序的一部分。 (KnownTypeAttribute可以使用方法名称,并且可以使用一些反射来获取类型。)这是向联合添加一小段代码以获得序列化的一种非常简单的方法。

open Microsoft.FSharp.Reflection 
open System.Reflection 
open System.Runtime.Serialization 
open System.Xml

[<KnownType("KnownTypes")>]
type Union21WithKnownTypes = 
    | Case1 of int * int
    | Case2 of string
    static member KnownTypes() = 
        typeof<Union21WithKnownTypes>.GetNestedTypes(
            BindingFlags.Public 
            ||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion 

let dcs = new DataContractSerializer(typeof<Union21WithKnownTypes[]>)
let arr = [| Case1(1,1); Case2("2") |]
printfn "orig data: %A" arr
let sb = new System.Text.StringBuilder()
let xw = XmlWriter.Create(sb)
dcs.WriteObject(xw, arr)
xw.Close()
let s = sb.ToString()
printfn ""
printfn "encoded as: %s" s
printfn ""
let xr = XmlReader.Create(new System.IO.StringReader(s))
let o = dcs.ReadObject(xr)
printfn "final data: %A" o

这是JSON版本:

open Microsoft.FSharp.Reflection  
open System.Reflection  
open System.Runtime.Serialization  
open System.Runtime.Serialization.Json  
open System.Xml 

[<KnownType("KnownTypes")>] 
type Union21WithKnownTypes =  
    | Case1 of int * int 
    | Case2 of string 
    static member KnownTypes() =  
        typeof<Union21WithKnownTypes>.GetNestedTypes( 
            BindingFlags.Public  
            ||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion  

let dcs = new DataContractJsonSerializer(typeof<Union21WithKnownTypes[]>) 
let arr = [| Case1(1,1); Case2("2") |] 
printfn "orig data: %A" arr 
let stream = new System.IO.MemoryStream()
dcs.WriteObject(stream, arr) 
stream.Seek(0L, System.IO.SeekOrigin.Begin) |> ignore
let bytes = Array.create (int stream.Length) 0uy
stream.Read(bytes, 0, int stream.Length) |> ignore
let s = System.Text.Encoding.ASCII.GetString(bytes)
printfn "" 
printfn "encoded as: %s" s 
printfn "" 
stream.Seek(0L, System.IO.SeekOrigin.Begin) |> ignore
let o = dcs.ReadObject(stream)
printfn "final data: %A" o