Last active
July 21, 2016 12:18
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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