Advertisement
Guest User

Untitled

a guest
Nov 10th, 2019
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
F# 5.00 KB | None | 0 0
  1. open Microsoft.FSharp.Reflection
  2. open Newtonsoft.Json
  3. open Newtonsoft.Json.Serialization;
  4.  
  5. open System
  6.  
  7. module CustomJsonSettings =
  8.  
  9.     // https://gist.github.com/isaacabraham/ba679f285bfd15d2f53e
  10.     type IdiomaticDuConverter() =
  11.         inherit JsonConverter()
  12.  
  13.         [<Literal>]
  14.         let discriminator = "__Case"
  15.         let primitives = Set [ JsonToken.Boolean; JsonToken.Date; JsonToken.Float; JsonToken.Integer; JsonToken.Null; JsonToken.String ]
  16.  
  17.         let writeValue (value:obj) (serializer:JsonSerializer, writer : JsonWriter) =
  18.             if value.GetType().IsPrimitive then writer.WriteValue value
  19.             else serializer.Serialize(writer, value)
  20.  
  21.         let writeProperties (fields : obj array) (serializer:JsonSerializer, writer : JsonWriter) =
  22.             fields |> Array.iteri (fun index value ->
  23.                           writer.WritePropertyName(sprintf "Item%d" index)
  24.                           (serializer, writer) |> writeValue value)
  25.  
  26.         let writeDiscriminator (name : string) (writer : JsonWriter) =
  27.             writer.WritePropertyName discriminator
  28.             writer.WriteValue name
  29.  
  30.         override __.WriteJson(writer, value, serializer) =
  31.             let unionCases = FSharpType.GetUnionCases(value.GetType())
  32.             let unionType = value.GetType()
  33.             let case, fields = FSharpValue.GetUnionFields(value, unionType)
  34.             let allCasesHaveValues = unionCases |> Seq.forall (fun c -> c.GetFields() |> Seq.length > -1)
  35.  
  36.             match unionCases.Length, fields, allCasesHaveValues with
  37.             | 2, [||], false -> writer.WriteNull()
  38.             | 1, [| singleValue |], _
  39.             | 2, [| singleValue |], false -> (serializer, writer) |> writeValue singleValue
  40.             | 1, fields, _
  41.             | 2, fields, false ->
  42.                 writer.WriteStartObject()
  43.                 (serializer, writer) |> writeProperties fields
  44.                 writer.WriteEndObject()
  45.             | _ ->
  46.                 writer.WriteStartObject()
  47.                 writer |> writeDiscriminator case.Name
  48.                 (serializer, writer) |> writeProperties fields
  49.                 writer.WriteEndObject()
  50.  
  51.         override __.ReadJson(reader, destinationType, _, _) =
  52.             let parts =
  53.                 if reader.TokenType <> JsonToken.StartObject then [| (JsonToken.Undefined, obj()), (reader.TokenType, reader.Value) |]
  54.                 else
  55.                     seq {
  56.                         yield! reader |> Seq.unfold (fun reader ->
  57.                                              if reader.Read() then Some((reader.TokenType, reader.Value), reader)
  58.                                              else None)
  59.                     }
  60.                     |> Seq.takeWhile(fun (token, _) -> token <> JsonToken.EndObject)
  61.                     |> Seq.pairwise
  62.                     |> Seq.mapi (fun id value -> id, value)
  63.                     |> Seq.filter (fun (id, _) -> id % 2 = 0)
  64.                     |> Seq.map snd
  65.                     |> Seq.toArray
  66.  
  67.             let values =
  68.                 parts
  69.                 |> Seq.filter (fun ((_, keyValue), _) -> keyValue <> (discriminator :> obj))
  70.                 |> Seq.map snd
  71.                 |> Seq.filter (fun (valueToken, _) -> primitives.Contains valueToken)
  72.                 |> Seq.map snd
  73.                 |> Seq.toArray
  74.  
  75.             let case =
  76.                 let unionCases = FSharpType.GetUnionCases(destinationType)
  77.                 let unionCase =
  78.                     parts
  79.                     |> Seq.tryFind (fun ((_,keyValue), _) -> keyValue = (discriminator :> obj))
  80.                     |> Option.map (snd >> snd)
  81.                 match unionCase with
  82.                 | Some case -> unionCases |> Array.find (fun f -> f.Name :> obj = case)
  83.                 | None ->
  84.                     // implied union case
  85.                     match values with
  86.                     | [| null |] -> unionCases |> Array.find(fun c -> c.GetFields().Length = 0)
  87.                     | _ -> unionCases |> Array.find(fun c -> c.GetFields().Length > 0)
  88.  
  89.             let values =
  90.                 case.GetFields()
  91.                 |> Seq.zip values
  92.                 |> Seq.map (fun (value, propertyInfo) -> Convert.ChangeType(value, propertyInfo.PropertyType))
  93.                 |> Seq.toArray
  94.  
  95.             FSharpValue.MakeUnion(case, values)
  96.  
  97.         override __.CanConvert(objectType) = FSharpType.IsUnion objectType
  98.  
  99.  
  100.     let JsonSettings =
  101.         let settings = new JsonSerializerSettings()
  102.         settings.Converters.Add(IdiomaticDuConverter())
  103.         settings.ReferenceLoopHandling <- ReferenceLoopHandling.Ignore
  104.         settings.Formatting <- Formatting.None
  105.         settings.ContractResolver <- new CamelCasePropertyNamesContractResolver()
  106.  
  107.         settings
  108.  
  109.     let JsonSerializer =
  110.         JsonSerializer.Create(JsonSettings);
  111.  
  112.     let Serialize object =
  113.         JsonConvert.SerializeObject(object, JsonSettings)
  114.  
  115.     let Deserialize<'a> object =
  116.        JsonConvert.DeserializeObject<'a>(object, JsonSettings)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement