Skip to content

Instantly share code, notes, and snippets.

@mariomeyrelles
Last active July 21, 2016 12:18
Show Gist options
  • Save mariomeyrelles/c96a58453b182352333c2d77b45ce535 to your computer and use it in GitHub Desktop.
Save mariomeyrelles/c96a58453b182352333c2d77b45ce535 to your computer and use it in GitHub Desktop.
A first idea on how to serialize an F# object and get a list of parameters to be used with Neo4j
type Neo4jParameterSerializer () =
member private this.readDateField (entity) (p: PropertyInfo) =
p.Name, (p.GetValue(entity) :?> DateTime).ToString("yyyy-MM-dd HH:mm:ss") :> obj
member private this.readSimpleField entity (p: PropertyInfo) = p.Name, p.GetValue(entity).ToString() :> obj
member private this.readUnionField entity (p:PropertyInfo) =
// todo: support more complex discriminated unions.
let case = FSharpType.GetUnionCases(p.PropertyType).First()
let propValue = p.GetValue(entity)
let caseInfo, fields = FSharpValue.GetUnionFields(propValue, p.PropertyType)
p.Name, fields.[0]
member private this.getValueFromField parentEntity (prop:PropertyInfo) = prop.GetValue(parentEntity)
member private this.readGenericField parentEntity (prop:PropertyInfo) =
let genericField = prop.GetValue(parentEntity)
let innerFieldsInfo = genericField.GetType().GetProperties()
let objectsOfGenericField = innerFieldsInfo
|> Array.map (fun p -> (this.getValueFromField genericField p))
|> Array.map this.readParamsFromObj
|> List.ofArray
|> List.concat
objectsOfGenericField
member private this.readRecordField entity (prop:PropertyInfo) =
match prop with
| p when FSharpType.IsUnion(p.PropertyType) -> [ this.readUnionField entity prop ]
| p when p.PropertyType.IsGenericType -> this.readGenericField entity prop
| p when FSharpType.IsRecord(p.PropertyType) -> this.readRecordField entity prop
| p when FSharpType.IsRecord(p.DeclaringType) ->
match p with
| p' when p'.PropertyType = typeof<DateTime> -> [ this.readDateField entity prop ]
| _ -> [ this.readSimpleField entity prop ]
| _ -> failwith "type not supported yet"
/// Returns a list of string * obj with all the properties and values of the passed-in entity to be save on Neo4j.
member this.readParamsFromObj entity =
let fields = FSharpType.GetRecordFields(entity.GetType()) |> List.ofArray
// tail-recursive function with an acumulator of values read so far.
let rec readField f keyValuePairsReadSoFar =
match f with
| [] -> keyValuePairsReadSoFar
| x::xs ->
let dataFromField = this.readRecordField entity x
let dataAppended = dataFromField @ keyValuePairsReadSoFar
readField xs dataAppended
readField fields []
/// Motivation: I needed to get all the fields and values of a F# object in order to pass them to Neo4j as parameters.
/// Process: The idea is to navigate recursively through the properties and read each property name and value. The name
/// of the field must be the same name of a Neo4j parameter. If the serializer finds a more complex object, it tries to
/// investigate inner objects and get the properties of child objects.
/// Limitations: Actually I'm working on this and I know that if find exactly same names on different objects, probably there
/// will be a bug. Also, I can only process single case discriminated unions with a single field.
let asNeo4jParameters (entity: 'T) =
let serializer = new Neo4jParameterSerializer()
serializer.readParamsFromObj entity |> dict
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment