Advertisement
Guest User

Untitled

a guest
Jan 22nd, 2019
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
F# 13.96 KB | None | 0 0
  1. // Learn more about F# at http://fsharp.org
  2.  
  3. open System
  4. open FParsec
  5. open System.Text.RegularExpressions
  6. open Newtonsoft.Json
  7. open Microsoft.FSharp
  8. open Microsoft.FSharp.Reflection
  9.  
  10. let inline (^) f x = f x
  11. let (<||>) f1 f2 x = f1 x || f2 x
  12. let (<&&>) f1 f2 x = f1 x && f2 x
  13.  
  14. type Json =
  15.     | JBool
  16.     | JBoolOption
  17.     | JNull
  18.     | JInt
  19.     | JIntOption
  20.     | JFloat
  21.     | JFloatOption
  22.     | JString
  23.     | JDateTimeOffset
  24.     | JDateTimeOffsetOption
  25.     | JStringOption
  26.     | JList of Json list
  27.     | JEmptyObjectOption
  28.     | JObject of (string * Json) list
  29.     | JObjectOption of (string * Json) list
  30.     | JArray of Json
  31.     | JArrayOption of Json
  32.  
  33. [<RequireQualifiedAccess>]
  34. type CollectionType =
  35.     | Array
  36.     | List
  37.     | CSharpList
  38.    
  39. let listGenerator = sprintf "%s list"
  40. let arrayGenerator = sprintf "%s array"
  41. let charpListGenerator = sprintf "List<%s>"
  42.  
  43. type TypeIdentification =
  44.     { Name: string
  45.       Attributes: string list}
  46.  
  47. type FieldIdentification =
  48.     { Name: string
  49.       Type: string
  50.       Attributes: string list }
  51.  
  52. type TypeDefinition =
  53.     { TypeIdentification: TypeIdentification
  54.       FieldIdentifications: FieldIdentification list }
  55.  
  56. let ws = spaces
  57. let str s = pstring s
  58.  
  59. let stringLiteral =
  60.     let escape = anyOf "\"\\/bfnrt"
  61.                   |>> function
  62.                       | 'b' -> "\b"
  63.                       | 'f' -> "\u000C"
  64.                       | 'n' -> "\n"
  65.                       | 'r' -> "\r"
  66.                       | 't' -> "\t"
  67.                       | c   -> string c
  68.  
  69.     let unicodeEscape =
  70.         str "u" >>. pipe4 hex hex hex hex (fun h3 h2 h1 h0 ->
  71.             let hex2int c = (int c &&& 15) + (int c >>> 6)*9
  72.             (hex2int h3)*4096 + (hex2int h2)*256 + (hex2int h1)*16 + hex2int h0
  73.             |> char |> string
  74.         )
  75.  
  76.     between (str "\"") (str "\"")
  77.             (stringsSepBy (manySatisfy (fun c -> c <> '"' && c <> '\\'))
  78.                           (str "\\" >>. (escape <|> unicodeEscape)))
  79.  
  80. let stringOrDateTime (str: string) =
  81.     if DateTimeOffset.TryParse(str, ref (DateTimeOffset())) then JDateTimeOffset
  82.     else JString
  83.  
  84. let jstringOrDate = stringLiteral |>> stringOrDateTime
  85.  
  86. let jnumber = pfloat |>> (fun x -> if x = Math.Floor(x) then JInt else JFloat)
  87.  
  88. let jtrue  = stringReturn "true"  JBool
  89. let jfalse = stringReturn "false" JBool
  90. let jnull  = stringReturn "null" JNull
  91.  
  92. let jvalue, jvalueRef = createParserForwardedToRef()
  93.  
  94. let listBetweenStrings sOpen sClose pElement f =
  95.     between (str sOpen) (str sClose)
  96.             (ws >>. sepBy (pElement .>> ws) (str "," .>> ws) |>> f)
  97.  
  98. let keyValue = tuple2 stringLiteral (ws >>. str ":" >>. ws >>. jvalue)
  99.  
  100. let jlist   = listBetweenStrings "[" "]" jvalue JList
  101. let jobject = listBetweenStrings "{" "}" keyValue JObject
  102.  
  103. do jvalueRef := choice [jobject
  104.                         jlist
  105.                         jstringOrDate
  106.                         jnumber
  107.                         jtrue
  108.                         jnull
  109.                         jfalse]
  110.  
  111. let json = ws >>. jvalue .>> ws .>> eof
  112.  
  113. let parseJsonString str = run json str
  114.  
  115. let rec aggreagateListToSingleType jsonList =
  116.     let isDateTime = function JDateTimeOffset _ | JDateTimeOffsetOption _ -> true | _ -> false
  117.     let isArray = function JArray _ | JArrayOption _ -> true | _ -> false
  118.     let isList = function JList _ -> true | _ -> false
  119.     let isString = function JStringOption | JString -> true | _ -> false
  120.     let isNumber = function JInt | JFloat| JIntOption | JFloatOption -> true | _ -> false
  121.     let isNull = function JNull -> true | _ -> false
  122.     let isBool = function JBool | JBoolOption -> true | _ -> false
  123.     let isObject = function JObject _ | JObjectOption _ -> true | _ -> false
  124.  
  125.     let isDateTimeOption = function JDateTimeOffsetOption -> true | _ -> false
  126.     let isBoolOption = function JBoolOption -> true | _ -> false
  127.     let isStringOption = function JStringOption -> true | _ -> false
  128.     let isArrayOption = function JArrayOption _ -> true | _ -> false
  129.     let isObjectOption = function JObjectOption _ -> true | _ -> false
  130.     let isNumberOption = function JIntOption | JFloatOption -> true | _ -> false
  131.     let typeOrder =
  132.         function
  133.         | JInt | JIntOption -> 1
  134.         | JFloat | JFloatOption -> 2
  135.         | _ -> failwith "Not number type"
  136.    
  137.     let checkStringOption = List.exists (isNull <||> isStringOption)
  138.     let checkArrayOption = List.exists (isNull <||> isArrayOption)
  139.     let checkObjectOption = List.exists (isNull <||> isObjectOption)
  140.     let checkNumberOption = List.exists (isNull <||> isNumberOption)
  141.     let checkBoolOption = List.exists (isNull <||> isBoolOption)
  142.     let checkDateTimeOption = List.exists (isNull <||> isDateTimeOption)
  143.  
  144.     let getOptionType istanceType isOption =
  145.         match istanceType, isOption with
  146.         | JInt, true -> JIntOption
  147.         | JFloat, true -> JFloatOption
  148.         | JBool, true -> JBoolOption
  149.         | JString, true -> JStringOption
  150.         | JDateTimeOffset, true -> JDateTimeOffsetOption
  151.         | JObject x, true -> JObjectOption x
  152.         | JArray x, true -> JArrayOption x
  153.         | x, _ -> x
  154.  
  155.     match jsonList with
  156.     | [] -> JEmptyObjectOption
  157.     | list when list |> List.forall isNull -> JEmptyObjectOption
  158.     | list when list |> List.forall (isNumber <||> isNull) ->
  159.         let newType =
  160.                 list
  161.                 |> List.filter (not << isNull)
  162.                 |> List.distinct
  163.                 |> List.map(fun x -> (x, typeOrder x))
  164.                 |> List.maxBy (fun (_, rank) -> rank)
  165.                 |> fst
  166.         getOptionType newType (list |> checkNumberOption)
  167.  
  168.     | list when list |> List.forall (isString <||> isNull) ->
  169.         getOptionType JString (list |> checkStringOption)
  170.  
  171.     | list when list |> List.forall (isDateTime <||> isNull) ->
  172.         getOptionType JDateTimeOffset (list |> checkDateTimeOption)
  173.  
  174.     | list when list |> List.forall (isBool <||> isNull) ->
  175.         getOptionType JBool (list |> checkBoolOption)
  176.  
  177.     | list when list |> List.forall (isObject <||> isNull) ->
  178.         let getObjects =
  179.             List.filter (not << isNull)
  180.             >> List.map(function JObject list | JObjectOption list -> list | _ -> failwith "Excpected JObject")
  181.  
  182.         let res =
  183.             list
  184.             |> getObjects
  185.             |> List.collect(fun x -> x)
  186.             |> List.groupBy(fun (key, _) -> key)
  187.             |> List.map(fun (key, value) -> (key, (aggreagateListToSingleType (value |> List.map snd))))
  188.         getOptionType (JObject res) (list |> checkObjectOption)
  189.  
  190.     | list when list |> List.forall (isList <||> isNull) ->
  191.         let getLists = List.filter (not << isNull) >> List.map(function JList x -> x | _ -> failwith "Excpected JList")
  192.  
  193.         let res =
  194.             list
  195.             |> getLists
  196.             |> List.collect(fun x -> x)            
  197.             |> aggreagateListToSingleType
  198.            
  199.         getOptionType (JArray res) (list |> checkArrayOption)    
  200.  
  201.     | list when list |> List.forall (isArray <||> isNull) ->
  202.         let getObjs =
  203.             List.filter (not << isNull)
  204.             >> List.map(function JArray list | JArrayOption list -> list | _ -> failwith "Excpected JArray")
  205.  
  206.         let res =
  207.             list
  208.             |> getObjs
  209.             |> aggreagateListToSingleType
  210.  
  211.         getOptionType (JArray res) (list |> checkArrayOption)    
  212.     | _ -> JEmptyObjectOption
  213.  
  214. let rec castArray =
  215.      List.map(fun (key, value) ->
  216.                 match value with
  217.                 | JObject list -> key, JObject ^ castArray list
  218.                 | JList list -> key, JArray ^ aggreagateListToSingleType list
  219.                 | _ -> key, value)
  220.  
  221. let fixName (name: string) =
  222.     let getFirstChar (name: string) = name.Chars 0
  223.     let toUpperFirst name = ((Char.ToUpper ^ getFirstChar name) |> Char.ToString) + name.Substring 1
  224.     let newFieldName = Regex.Replace(name, "[!@#$%^&*()\-=+|/><\[\]\.\\*`]+", "") |> toUpperFirst
  225.  
  226.     if (not <| Char.IsLetter ^ getFirstChar name) && getFirstChar name <> '_' then
  227.         "The" + newFieldName
  228.     else
  229.         newFieldName
  230.  
  231. let rec translateToString listGenerator =
  232.     let getName { Name = name; Type = _; Attributes = _ } = name
  233.  
  234.     fixName
  235.     >> fun name ->
  236.         function
  237.         | JBool ->                  { Name = name; Type = "bool"; Attributes = [] }
  238.         | JBoolOption ->            { Name = name; Type = "bool option"; Attributes = [] }
  239.         | JNull ->                  { Name = name; Type = "Object option"; Attributes = [] }
  240.         | JInt ->                   { Name = name; Type = "int64"; Attributes = [] }
  241.         | JIntOption ->             { Name = name; Type = "int64 option"; Attributes = [] }
  242.         | JFloat ->                 { Name = name; Type = "float"; Attributes = [] }
  243.         | JFloatOption ->           { Name = name; Type = "float option"; Attributes = [] }
  244.         | JString ->                { Name = name; Type = "string"; Attributes = [] }
  245.         | JDateTimeOffset ->        { Name = name; Type = "DateTimeOffset"; Attributes = [] }
  246.         | JDateTimeOffsetOption ->  { Name = name; Type = "DateTimeOffset option"; Attributes = [] }
  247.         | JStringOption ->          { Name = name; Type = "string option"; Attributes = [] }
  248.         | JEmptyObjectOption ->     { Name = name; Type = "Object option"; Attributes = [] }
  249.         | JObject _ ->              { Name = name; Type = name; Attributes = [] }
  250.         | JObjectOption _ ->        { Name = name; Type = sprintf "%s %s" name "option"; Attributes = [] }
  251.         | JArray obj ->             { Name = name; Type = translateToString listGenerator name obj |> getName |> listGenerator; Attributes = [] }
  252.         | JArrayOption obj ->       { Name = name; Type = translateToString listGenerator name obj |> getName |> listGenerator; Attributes = [] }
  253.         | _ -> failwith "translateToString unexcpected"
  254.  
  255. let rec extractObject json =
  256.     match json with
  257.     | JArray obj
  258.     | JArrayOption obj -> extractObject obj
  259.     | JObject _
  260.     | JObjectOption _  -> Some json
  261.     | _ -> None
  262.  
  263. let rec deep listGenerator node =
  264.     let rec tailDeep acc jobjs =
  265.         match jobjs with
  266.         | [] -> acc |> List.distinctBy(fun { TypeIdentification = { Name = name } ; FieldIdentifications = _ } -> name)
  267.         | (name, (JObject list))::xs
  268.         | (name, (JObjectOption list))::xs ->
  269.             let newType =
  270.                 list
  271.                 |> List.distinctBy (fun (name, _) -> name)
  272.                 |> List.map (fun (name, value) -> translateToString listGenerator name value)
  273.                 |> fun x -> { TypeIdentification = { Name = name |> fixName; Attributes = [] }; FieldIdentifications = x }
  274.  
  275.             let newJobjs =
  276.                 list
  277.                 |> List.map(fun (key, value) -> (key, extractObject value))
  278.                 |> List.choose(fun (key, v) -> match v with Some j -> Some (key, j) | None -> None)
  279.                        
  280.             tailDeep (newType::acc) (newJobjs @ xs)
  281.         | _ -> failwith "unexpected"
  282.  
  283.     tailDeep [] [node]
  284.  
  285. let typeDefinitionToType typeDefinition =
  286.     let joinByNewLine = sprintf "%s\n%s"
  287.  
  288.     let typeDef =
  289.         match typeDefinition.TypeIdentification.Attributes with
  290.         | [] -> sprintf "type %s =" typeDefinition.TypeIdentification.Name
  291.         | attributes ->
  292.              attributes
  293.              |> List.fold joinByNewLine ""
  294.              |> (fun x -> sprintf "%s\ntype %s =" x typeDefinition.TypeIdentification.Name)
  295.  
  296.     let fieldDefinitionToType fieldDefinition =
  297.         match fieldDefinition.Attributes with
  298.         | [] -> sprintf "%s: %s" fieldDefinition.Name fieldDefinition.Type
  299.         | attributes -> sprintf "\n%s\n%s: %s" (attributes |> List.reduce joinByNewLine) fieldDefinition.Name fieldDefinition.Type
  300.  
  301.     let fieldDef =
  302.         match typeDefinition.FieldIdentifications with
  303.         | [] -> ""
  304.         | fields ->
  305.                 fields
  306.                 |> List.map fieldDefinitionToType
  307.                 |> List.reduce (sprintf "%s\n\t  %s")
  308.  
  309.     sprintf "%s\n\t{ %s }" typeDef fieldDef
  310.  
  311. let typesToView =
  312.     List.map typeDefinitionToType
  313.     >> List.reduce (sprintf "%s\n\n%s")
  314.  
  315. type OptionConverter() =
  316.     inherit JsonConverter()
  317.    
  318.     override x.CanConvert(t) =
  319.         t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
  320.  
  321.     override x.WriteJson(writer, value, serializer) =
  322.         let value =
  323.             if value = null then null
  324.             else
  325.                 let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
  326.                 fields.[0]  
  327.         serializer.Serialize(writer, value)
  328.  
  329.     override x.ReadJson(reader, t, existingValue, serializer) =        
  330.         let innerType = t.GetGenericArguments().[0]
  331.         let innerType =
  332.             if innerType.IsValueType then (typedefof<Nullable<_>>).MakeGenericType([|innerType|])
  333.             else innerType        
  334.         let value = serializer.Deserialize(reader, innerType)
  335.         let cases = FSharpType.GetUnionCases(t)
  336.         if value = null then FSharpValue.MakeUnion(cases.[0], [||])
  337.         else FSharpValue.MakeUnion(cases.[1], [|value|])
  338.  
  339. let generateRecords (str: string) mainObject =
  340.     match parseJsonString str with
  341.     | Success(result, _, _)   ->
  342.         let rootObject =
  343.             match mainObject with
  344.             | Some x -> x
  345.             | None -> "RootObject"
  346.  
  347.         match castArray [rootObject |> fixName, result] with
  348.         | [x] -> printfn "%s" ^ (deep listGenerator x |> typesToView)
  349.         | _ -> printfn "Failure"
  350.     | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg
  351.  
  352. [<EntryPoint>]
  353. let main argv =
  354.    
  355.     let testExample = @"
  356.    {
  357.        ""employees"": [[{""name"": ""2012-04-23T18:25:43.511Z""}, {""name"": null} ]],
  358.        ""employees2"": [[{""name"": ""2012-04-23T18:25:43.511Z""}, {""name"": null} ]]    
  359.    }"
  360.  
  361.     generateRecords testExample None
  362.  
  363.     Console.ReadKey() |> ignore
  364.     0 // return an integer exit code
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement