Thursday, May 07, 2015

Getting the types in an F# union type

This arose in the context of performing data contract serialization of a union type, and needing to feed the concrete types in as known types. As the FSharp.Reflection facilities only let you at the names of the union cases, it's necessary to rely on the secret knowledge that the union cases are implemented as nested subtypes of the abstract union type

open System
open Microsoft.FSharp.Reflection
let GetUnionTypes (t:Type) =
seq { yield t
if FSharpType.IsUnion t
then yield! FSharpType.GetUnionCases t
// implementation detail leaks here -- nested type name
|> Seq.map (fun x -> (string t) + "+" + x.Name)
|> Seq.map Type.GetType }
type expr = Num of int
| Operation of (expr * expr)
let baseType = typeof<expr>
GetUnionTypes baseType
|> Seq.iter (printfn "%A")
;;
view raw gistfile1.fs hosted with ❤ by GitHub


Then we can serialize objects of the union type like

open System.IO
open System.Runtime.Serialization.Json
open System.Text
let exprTypes = GetUnionTypes baseType
let ToJson<'t> (myObj:'t) =
use ms = new MemoryStream()
DataContractJsonSerializer(typeof<'t>, exprTypes).WriteObject(ms, myObj)
Encoding.Default.GetString(ms.ToArray())
let FromJson<'t> (jsonString:string) : 't =
use ms = new MemoryStream(Encoding.Default.GetBytes(jsonString))
let obj = DataContractJsonSerializer(typeof<'t>, exprTypes).ReadObject(ms)
obj :?> 't
let value = Num 3
let json = ToJson value
let recovered : expr = FromJson json
;;
val exprTypes : seq<Type>
val ToJson : myObj:'t -> string
val FromJson : jsonString:string -> 't
val value : expr = Num 3
val json : string = "{"__type":"FSI_0002.expr.Num:#","item":3}"
val recovered : expr = Num 3
view raw gistfile1.fs hosted with ❤ by GitHub


where we feed the list of types to be known to the constructor for the serializer.


No comments :