views:

78

answers:

1

I need to persist an abstract syntax tree represented using F# discriminated unions to a human readable compact format, such as the format already used in the F# language to construct discriminated unions, that I can read back in to discriminated union instances later. I'm a bit surprised the F# library doesn't support this as it surely must be done in an efficient way already in the compiler and F# interactive.

Are there any free/open source implementations out there for doing this in a reasonable (but not necessarily extremely) efficient manner?

Note: I do not want an XML based serialization.

+3  A: 

EDIT: Neither of the answers below really meet your criteria, but I'm posting in case others looking for union-serialization find them useful. I don't know offhand of any library way to re-parse the output of sprintf "%A" on a union - keep in mind that the compiler and FSI have a much different task, knowing scopes and dealing with namespaces and qualified names and shadowing and whatnot, and even ignoring that, parsing the data carried by the unions (ints, strings, arbitrary objects, etc.) is potentially a whole task in itself.

Here is one strategy for union serialization, as part of a small sample program. (KnownTypeAttribute can take a method name, and you can use some reflection to get the types.) This is a very easy way to add a tiny bit of code to a union to get serialization.

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

Here's the JSON version:

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 
Brian