fbehrens
fbehrens

Reputation: 6543

How to extract a Record from a Generic Dictionary?

Through some API i get access to kind of Table in the form of a sequence of Gereric.Dictionary. Because the table has a schema I want to extract records out of it. Here is what i have ?

open System.Collections.Generic

let gd = Dictionary<string,obj> ()
gd.Add("name", "Frank")
gd.Add("age", 24)
gd.Add("born", 1999)

type Person = { name : string
                age  : int}

let extractPerson (gd:Dictionary<string,obj>) = 
 { name = gd.["name"] :?> string
   age  = gd.["age"] :?> int}

Can I make the function extractPerson more generic, like

let extract<'T> (gd:Dictionary<string,obj>) =
  // ???

so that I can call ?

extract<Person> gd

Upvotes: 0

Views: 445

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243061

As mentioned in the comments, this is something that can only be done using reflection. The following works for your simple example, but it is very limited:

open Microsoft.FSharp.Reflection

let extract<'T>(gd:Dictionary<string,obj>) = 
  let flds = FSharpType.GetRecordFields(typeof<'T>)
  let vals = [| for f in flds -> gd.[f.Name] |]
  FSharpValue.MakeRecord(typeof<'T>, vals) :?> 'T

It uses GetRecordFields to find out what the names of the fields are, then it gets their values from the dictionary and calls MakeRecord to create the record value. This is not super efficient, but depending on what you need, it might just work good enough for you.

Now you can use it as follows:

let gd = Dictionary<string,obj> ()
gd.Add("name", "Frank")
gd.Add("age", 24)
gd.Add("born", 1999)

type Person = { name : string; age  : int}    
extract<Person> gd

Upvotes: 5

Related Questions