Advertisement
Guest User

Untitled

a guest
Jan 24th, 2019
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
F# 19.18 KB | None | 0 0
  1. open System
  2. open FParsec
  3. open System.Text.RegularExpressions
  4. open Newtonsoft.Json
  5. open Microsoft.FSharp
  6. open Microsoft.FSharp.Reflection
  7. open System.Reflection.Metadata
  8.  
  9. let inline (^) f x = f x
  10. let (<||>) f1 f2 x = f1 x || f2 x
  11. let (<&&>) f1 f2 x = f1 x && f2 x
  12.  
  13. type Json =
  14.     | JBool
  15.     | JBoolOption
  16.     | JNull
  17.     | JInt
  18.     | JIntOption
  19.     | JFloat
  20.     | JFloatOption
  21.     | JString
  22.     | JDateTimeOffset
  23.     | JDateTimeOffsetOption
  24.     | JStringOption
  25.     | JList of Json list
  26.     | JEmptyObjectOption
  27.     | JObject of (string * Json) list
  28.     | JObjectOption of (string * Json) list
  29.     | JArray of Json
  30.     | JArrayOption of Json
  31.  
  32. [<RequireQualifiedAccess>]
  33. type CollectionType =
  34.     | Array
  35.     | List
  36.     | CSharpList
  37.  
  38. [<RequireQualifiedAccess>]
  39. type GenerationType =
  40.     | JustTypes
  41.     | NewtonsoftJsonProperties
  42.  
  43. let listGenerator = sprintf "%s list"
  44. let arrayGenerator = sprintf "%s array"
  45. let charpListGenerator = sprintf "List<%s>"
  46.  
  47. type TypeInfo =
  48.     { Name: string
  49.       Attributes: string list}
  50.  
  51. type FieldInfo =
  52.     { Name: string
  53.       Type: string
  54.       Attributes: string list }
  55.  
  56. type Type =
  57.     { TypeIdentification: TypeInfo
  58.       FieldIdentifications: FieldInfo list }
  59.  
  60. type NewtonsoftFile =
  61.     { ModuleName: string
  62.       Opens: string seq
  63.       TypeDefinitions: Type list }
  64.  
  65. type OutputType =
  66.     | NewtonsoftType of NewtonsoftFile
  67.     | JustTypes of Type list
  68.  
  69. type HandlerProvider =
  70.    { TypeHandler: (string -> FieldInfo list -> Type)
  71.      FieldHandler: (string -> Json -> FieldInfo)
  72.      FileHandler: (Type list -> OutputType)}
  73.    
  74. let ws = spaces
  75. let str s = pstring s
  76.  
  77. let stringLiteral =
  78.     let escape = anyOf "\"\\/bfnrt"
  79.                   |>> function
  80.                       | 'b' -> "\b"
  81.                       | 'f' -> "\u000C"
  82.                       | 'n' -> "\n"
  83.                       | 'r' -> "\r"
  84.                       | 't' -> "\t"
  85.                       | c   -> string c
  86.  
  87.     let unicodeEscape =
  88.         str "u" >>. pipe4 hex hex hex hex (fun h3 h2 h1 h0 ->
  89.             let hex2int c = (int c &&& 15) + (int c >>> 6)*9
  90.             (hex2int h3)*4096 + (hex2int h2)*256 + (hex2int h1)*16 + hex2int h0
  91.             |> char |> string
  92.         )
  93.  
  94.     between (str "\"") (str "\"")
  95.             (stringsSepBy (manySatisfy (fun c -> c <> '"' && c <> '\\'))
  96.                           (str "\\" >>. (escape <|> unicodeEscape)))
  97.  
  98. let stringOrDateTime (str: string) =
  99.     if DateTimeOffset.TryParse(str, ref (DateTimeOffset())) then JDateTimeOffset
  100.     else JString
  101.  
  102. let jstringOrDate = stringLiteral |>> stringOrDateTime
  103.  
  104. let jnumber = pfloat |>> (fun x -> if x = Math.Floor(x) then JInt else JFloat)
  105.  
  106. let jtrue  = stringReturn "true"  JBool
  107. let jfalse = stringReturn "false" JBool
  108. let jnull  = stringReturn "null" JNull
  109.  
  110. let jvalue, jvalueRef = createParserForwardedToRef()
  111.  
  112. let listBetweenStrings sOpen sClose pElement f =
  113.     between (str sOpen) (str sClose)
  114.             (ws >>. sepBy (pElement .>> ws) (str "," .>> ws) |>> f)
  115.  
  116. let keyValue = tuple2 stringLiteral (ws >>. str ":" >>. ws >>. jvalue)
  117.  
  118. let jlist   = listBetweenStrings "[" "]" jvalue JList
  119. let jobject = listBetweenStrings "{" "}" keyValue JObject
  120.  
  121. do jvalueRef := choice [jobject
  122.                         jlist
  123.                         jstringOrDate
  124.                         jnumber
  125.                         jtrue
  126.                         jnull
  127.                         jfalse]
  128.  
  129. let json = ws >>. jvalue .>> ws .>> eof
  130.  
  131. let parseJsonString str = run json str
  132.  
  133. let isDateTimeOffset = function JDateTimeOffset _ | JDateTimeOffsetOption _ -> true | _ -> false
  134. let isArray = function JArray _ | JArrayOption _ -> true | _ -> false
  135. let isList = function JList _ -> true | _ -> false
  136. let isString = function JStringOption | JString -> true | _ -> false
  137. let isNumber = function JInt | JFloat| JIntOption | JFloatOption -> true | _ -> false
  138. let isNull = function JNull -> true | _ -> false
  139. let isBool = function JBool | JBoolOption -> true | _ -> false
  140. let isObject = function JObject _ | JObjectOption _ -> true | _ -> false
  141.  
  142. let isDateTimeOption = function JDateTimeOffsetOption -> true | _ -> false
  143. let isBoolOption = function JBoolOption -> true | _ -> false
  144. let isStringOption = function JStringOption -> true | _ -> false
  145. let isArrayOption = function JArrayOption _ -> true | _ -> false
  146. let isObjectOption = function JObjectOption _ -> true | _ -> false
  147. let isNumberOption = function JIntOption | JFloatOption -> true | _ -> false
  148. let typeOrder =
  149.     function
  150.     | JInt | JIntOption -> 1
  151.     | JFloat | JFloatOption -> 2
  152.     | _ -> failwith "Not number type"
  153.  
  154. let checkStringOption = List.exists (isNull <||> isStringOption)
  155. let checkArrayOption = List.exists (isNull <||> isArrayOption)
  156. let checkObjectOption = List.exists (isNull <||> isObjectOption)
  157. let checkNumberOption = List.exists (isNull <||> isNumberOption)
  158. let checkBoolOption = List.exists (isNull <||> isBoolOption)
  159. let checkDateTimeOption = List.exists (isNull <||> isDateTimeOption)
  160.  
  161. let (|EmptyList|_|) =
  162.     function
  163.     | [] -> Some EmptyList
  164.     | _ -> None
  165.  
  166. let (|NullList|_|) =
  167.     function
  168.     | list when list |> List.forall isNull -> Some ^ NullList
  169.     | _ -> None
  170.  
  171. let (|NumberList|_|) =
  172.     function
  173.     | list when list |> List.forall (isNumber <||> isNull) -> Some ^ NumberList list
  174.     | _ -> None
  175.  
  176. let (|StringList|_|) =
  177.     function
  178.     | list when list |> List.forall (isString <||> isNull) -> Some ^ StringList list
  179.     | _ -> None
  180.  
  181. let (|DateTimeOffsetList|_|) =
  182.     function
  183.     | list when list |> List.forall (isDateTimeOffset <||> isNull) -> Some ^ DateTimeOffsetList list
  184.     | _ -> None
  185.  
  186. let (|BoolList|_|) =
  187.     function
  188.     | list when list |> List.forall (isBool <||> isNull) -> Some ^ BoolList list
  189.     | _ -> None
  190.  
  191. let (|ObjectList|_|) =
  192.     function
  193.     | list when list |> List.forall (isObject <||> isNull) -> Some ^ ObjectList list
  194.     | _ -> None
  195.  
  196. let (|ListList|_|) =
  197.     function
  198.     | list when list |> List.forall (isList <||> isNull) -> Some ^ ListList list
  199.     | _ -> None
  200.  
  201. let (|ArrayList|_|) =
  202.     function
  203.     | list when list |> List.forall (isArray <||> isNull) -> Some ^ ArrayList list
  204.     | _ -> None
  205.  
  206. let rec aggreagateListToSingleType jsonList =
  207.     let getOptionType isOption istanceType =
  208.         match istanceType, isOption with
  209.         | JInt, true -> JIntOption
  210.         | JFloat, true -> JFloatOption
  211.         | JBool, true -> JBoolOption
  212.         | JString, true -> JStringOption
  213.         | JDateTimeOffset, true -> JDateTimeOffsetOption
  214.         | JObject x, true -> JObjectOption x
  215.         | JArray x, true -> JArrayOption x
  216.         | x, _ -> x
  217.  
  218.     match jsonList with
  219.     | EmptyList -> JEmptyObjectOption
  220.     | NullList -> JEmptyObjectOption
  221.     | StringList list -> JString |> getOptionType (list |> checkStringOption)
  222.     | DateTimeOffsetList list -> JDateTimeOffset |> getOptionType (list |> checkDateTimeOption)
  223.     | BoolList list -> JBool |> getOptionType (list |> checkBoolOption)
  224.     | NumberList list ->
  225.             list
  226.             |> List.filter (not << isNull)
  227.             |> List.distinct
  228.             |> List.map(fun x -> (x, typeOrder x))
  229.             |> List.maxBy snd
  230.             |> fst
  231.             |> getOptionType (list |> checkNumberOption)
  232.     | ObjectList list ->
  233.             list
  234.             |> List.filter (not << isNull)
  235.             |> List.map(function JObject list | JObjectOption list -> list)
  236.             |> List.collect id
  237.             |> List.groupBy fst
  238.             |> List.map(fun (key, value) -> (key, (aggreagateListToSingleType (value |> List.map snd))))
  239.             |> JObject
  240.             |> getOptionType (list |> checkObjectOption)
  241.     | ListList list ->
  242.             list
  243.             |> List.filter (not << isNull)
  244.             |> List.map(function JList x -> x)
  245.             |> List.collect id          
  246.             |> aggreagateListToSingleType
  247.             |> JArray
  248.             |> getOptionType (list |> checkArrayOption)    
  249.     | ArrayList list ->
  250.             list
  251.             |> List.filter (not << isNull)
  252.             |> List.map(function JArray list | JArrayOption list -> list)
  253.             |> aggreagateListToSingleType
  254.             |> JArray
  255.             |> getOptionType (list |> checkArrayOption)
  256.     | _ -> JEmptyObjectOption
  257.  
  258. let rec castArray =
  259.      List.map(fun (key, value) ->
  260.                 match value with
  261.                 | JObject list -> key, JObject ^ castArray list
  262.                 | JList list -> key, JArray ^ aggreagateListToSingleType list
  263.                 | _ -> key, value)
  264.  
  265. let fixName (name: string) =
  266.     let getFirstChar (name: string) = name.Chars 0
  267.     let toUpperFirst name = ((Char.ToUpper ^ getFirstChar name) |> Char.ToString) + name.Substring 1
  268.     let newFieldName = Regex.Replace(name, "[!@#$%^&*()\-=+|/><\[\]\.\\*`]+", "") |> toUpperFirst
  269.  
  270.     if (not <| Char.IsLetter ^ getFirstChar name) && getFirstChar name <> '_' then
  271.         "The" + newFieldName
  272.     else
  273.         newFieldName
  274.  
  275. let rec extractObject json =
  276.     match json with
  277.     | JArray obj
  278.     | JArrayOption obj -> extractObject obj
  279.     | JObject _
  280.     | JObjectOption _  -> Some json
  281.     | _ -> None
  282.  
  283. let rec deep resultHandler node =
  284.     let rec tailDeep acc jobjs =
  285.         match jobjs with
  286.         | [] -> acc |> List.distinctBy(fun { TypeIdentification = { Name = name } ; FieldIdentifications = _ } -> name)
  287.         | (name, (JObject list))::xs
  288.         | (name, (JObjectOption list))::xs ->
  289.             let newType =
  290.                 list
  291.                 |> List.distinctBy (fun (name, _) -> name)
  292.                 |> List.map (fun (key, value) -> resultHandler.FieldHandler key value)
  293.                 |> fun x -> resultHandler.TypeHandler name x
  294.  
  295.             let newJobjs =
  296.                 list
  297.                 |> List.map(fun (key, value) -> (key, extractObject value))
  298.                 |> List.choose(fun (key, v) -> match v with Some j -> Some (key, j) | None -> None)
  299.                        
  300.             tailDeep (newType::acc) (newJobjs @ xs)
  301.         | _ -> failwith "unexpected"
  302.  
  303.     tailDeep [] [node] |> resultHandler.FileHandler
  304.  
  305. let joinByNewLine = sprintf "%s\n%s"
  306.  
  307. let typeIdentificationToString (typeIdentification: TypeInfo) =
  308.     match typeIdentification.Attributes with
  309.     | [] -> sprintf "type %s =" typeIdentification.Name
  310.     | attribues ->
  311.         attribues
  312.              |> List.fold joinByNewLine ""
  313.              |> (fun x -> sprintf "\t%s\ntype %s =" x typeIdentification.Name)
  314.  
  315. let fieldIdentificationToString (fieldIdentification: FieldInfo) =
  316.    match fieldIdentification.Attributes with
  317.    | [] -> sprintf "%s: %s" fieldIdentification.Name fieldIdentification.Type
  318.    | attributes -> sprintf "\n%s\n%s: %s" (attributes |> List.reduce joinByNewLine) fieldIdentification.Name fieldIdentification.Type
  319.    
  320. let fieldsToString =
  321.     function
  322.         | [] -> ""
  323.         | fields ->
  324.                 fields
  325.                 |> List.map fieldIdentificationToString
  326.                 |> List.reduce (sprintf "%s\n\t  %s")
  327.  
  328. let typeDefinitionToString typeDefinition =
  329.     sprintf "%s\n\t{ %s }" (typeIdentificationToString typeDefinition.TypeIdentification) (fieldsToString typeDefinition.FieldIdentifications)
  330.  
  331. let allTypeDefinitionsToString = List.map typeDefinitionToString >> List.reduce (sprintf "%s\n\n%s")
  332.  
  333. let opensToString<'a> = Seq.fold (sprintf "%s\n%s") ""
  334.    
  335. let newtonsoftToString file =
  336.    sprintf "module %s\n%s\n\n%s" file.ModuleName (opensToString file.Opens) (allTypeDefinitionsToString file.TypeDefinitions)
  337.    
  338. let toView =
  339.    function
  340.    | NewtonsoftType file -> file |> newtonsoftToString
  341.    | JustTypes typeList -> typeList |> allTypeDefinitionsToString
  342.  
  343. type OptionConverter() =
  344.    inherit JsonConverter()
  345.    
  346.    override x.CanConvert(t) =
  347.        t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
  348.  
  349.    override x.WriteJson(writer, value, serializer) =
  350.        let value =
  351.            if value = null then null
  352.            else
  353.                let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
  354.                fields.[0]  
  355.        serializer.Serialize(writer, value)
  356.  
  357.    override x.ReadJson(reader, t, existingValue, serializer) =        
  358.        let innerType = t.GetGenericArguments().[0]
  359.        let innerType =
  360.            if innerType.IsValueType then (typedefof<Nullable<_>>).MakeGenericType([|innerType|])
  361.            else innerType        
  362.        let value = serializer.Deserialize(reader, innerType)
  363.        let cases = FSharpType.GetUnionCases(t)
  364.        if value = null then FSharpValue.MakeUnion(cases.[0], [||])
  365.        else FSharpValue.MakeUnion(cases.[1], [|value|])
  366.  
  367. let newtonsoftGenerator =
  368.    let getName { Name = name; Type = _; Attributes = _ } = name
  369.  
  370.    let generateAttributes name = [sprintf "[<JsonProperty(\"%s\")>]" name]
  371.  
  372.    let rec fieldHandler listGenerator name = function
  373.        | JBool ->                  { Name = name |> fixName; Type = "bool"; Attributes = (generateAttributes name) }
  374.        | JBoolOption ->            { Name = name |> fixName; Type = "bool option"; Attributes = (generateAttributes name) }
  375.        | JNull ->                  { Name = name |> fixName; Type = "Object option"; Attributes = (generateAttributes name) }
  376.        | JInt ->                   { Name = name |> fixName; Type = "int64"; Attributes = (generateAttributes name) }
  377.        | JIntOption ->             { Name = name |> fixName; Type = "int64 option"; Attributes = (generateAttributes name) }
  378.        | JFloat ->                 { Name = name |> fixName; Type = "float"; Attributes = (generateAttributes name) }
  379.        | JFloatOption ->           { Name = name |> fixName; Type = "float option"; Attributes = (generateAttributes name) }
  380.        | JString ->                { Name = name |> fixName; Type = "string"; Attributes = (generateAttributes name) }
  381.        | JDateTimeOffset ->        { Name = name |> fixName; Type = "DateTimeOffset"; Attributes = (generateAttributes name) }
  382.        | JDateTimeOffsetOption ->  { Name = name |> fixName; Type = "DateTimeOffset option"; Attributes = (generateAttributes name) }
  383.        | JStringOption ->          { Name = name |> fixName; Type = "string option"; Attributes = (generateAttributes name) }
  384.        | JEmptyObjectOption ->     { Name = name |> fixName; Type = "Object option"; Attributes = (generateAttributes name) }
  385.        | JObject _ ->              { Name = name |> fixName; Type = name; Attributes = (generateAttributes name) }
  386.        | JObjectOption _ ->        { Name = name |> fixName; Type = sprintf "%s %s" name "option"; Attributes = (generateAttributes name) }
  387.        | JArray obj ->             { Name = name |> fixName; Type = fieldHandler listGenerator name obj |> getName |> listGenerator; Attributes = (generateAttributes name) }
  388.        | JArrayOption obj ->       { Name = name |> fixName; Type = fieldHandler listGenerator name obj |> getName |> listGenerator; Attributes = (generateAttributes name) }
  389.        | _ -> failwith "translateToString unexcpected"
  390.    
  391.    let typeHandler name fieldIndetifications =
  392.        { TypeIdentification = { Name = name |> fixName; Attributes = [] }; FieldIdentifications = fieldIndetifications }
  393.  
  394.    let opens =
  395.        [ "open System"
  396.          "open Newtonsoft.Json"]
  397.  
  398.    let fileHandler types =
  399.        { ModuleName = "Json2FSharp"
  400.          Opens = opens
  401.          TypeDefinitions = types }
  402.        |> NewtonsoftType
  403.  
  404.    { TypeHandler = typeHandler
  405.      FieldHandler = fieldHandler listGenerator
  406.      FileHandler = fileHandler }
  407.  
  408. let justTypesGenerator =
  409.    let getName { Name = name; Type = _; Attributes = _ } = name
  410.  
  411.    let generateAttributes _ = []
  412.  
  413.    let rec fieldHandler listGenerator name = function
  414.        | JBool ->                  { Name = name |> fixName; Type = "bool"; Attributes = (generateAttributes name) }
  415.        | JBoolOption ->            { Name = name |> fixName; Type = "bool option"; Attributes = (generateAttributes name) }
  416.        | JNull ->                  { Name = name |> fixName; Type = "Object option"; Attributes = (generateAttributes name) }
  417.        | JInt ->                   { Name = name |> fixName; Type = "int64"; Attributes = (generateAttributes name) }
  418.        | JIntOption ->             { Name = name |> fixName; Type = "int64 option"; Attributes = (generateAttributes name) }
  419.        | JFloat ->                 { Name = name |> fixName; Type = "float"; Attributes = (generateAttributes name) }
  420.        | JFloatOption ->           { Name = name |> fixName; Type = "float option"; Attributes = (generateAttributes name) }
  421.        | JString ->                { Name = name |> fixName; Type = "string"; Attributes = (generateAttributes name) }
  422.        | JDateTimeOffset ->        { Name = name |> fixName; Type = "DateTimeOffset"; Attributes = (generateAttributes name) }
  423.        | JDateTimeOffsetOption ->  { Name = name |> fixName; Type = "DateTimeOffset option"; Attributes = (generateAttributes name) }
  424.        | JStringOption ->          { Name = name |> fixName; Type = "string option"; Attributes = (generateAttributes name) }
  425.        | JEmptyObjectOption ->     { Name = name |> fixName; Type = "Object option"; Attributes = (generateAttributes name) }
  426.        | JObject _ ->              { Name = name |> fixName; Type = name; Attributes = (generateAttributes name) }
  427.        | JObjectOption _ ->        { Name = name |> fixName; Type = sprintf "%s %s" name "option"; Attributes = (generateAttributes name) }
  428.        | JArray obj ->             { Name = name |> fixName; Type = fieldHandler listGenerator name obj |> getName |> listGenerator; Attributes = (generateAttributes name) }
  429.        | JArrayOption obj ->       { Name = name |> fixName; Type = fieldHandler listGenerator name obj |> getName |> listGenerator; Attributes = (generateAttributes name) }
  430.        | _ -> failwith "translateToString unexcpected"
  431.    
  432.    let typeHandler name fieldIndetifications =
  433.        { TypeIdentification = { Name = name |> fixName; Attributes = [] }; FieldIdentifications = fieldIndetifications }
  434.  
  435.    { TypeHandler = typeHandler
  436.      FieldHandler = fieldHandler listGenerator
  437.      FileHandler = JustTypes}
  438.  
  439. let generateRecords fileHandler (str: string) mainObject =
  440.    match parseJsonString str with
  441.    | Success(result, _, _)   ->
  442.        let rootObject =
  443.            match mainObject with
  444.            | Some x -> x
  445.            | None -> "RootObject"
  446.  
  447.        match castArray [rootObject |> fixName, result] with
  448.        | [x] -> printfn "%s" ^ (deep fileHandler x |> toView)
  449.        | _ -> printfn "Failure"
  450.    | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg
  451.  
  452. [<EntryPoint>]
  453. let main argv =
  454.  
  455.    let getFileGenerator = function
  456.        | GenerationType.JustTypes -> justTypesGenerator
  457.        | GenerationType.NewtonsoftJsonProperties -> newtonsoftGenerator
  458.  
  459.    let testExample = @"
  460.    {
  461.        ""employees"": [[{""name"": ""2012-04-23T18:25:43.511Z""}, {""name"": null} ]],
  462.        ""employees2"": [[{""name"": ""2012-04-23T18:25:43.511Z""}, {""name"": null} ]]    
  463.    }"
  464.  
  465.    generateRecords (getFileGenerator GenerationType.NewtonsoftJsonProperties) testExample None
  466.  
  467.    Console.ReadKey() |> ignore
  468.    0 // return an integer exit code
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement