open System.Collections.Generic
open System.Runtime.Serialization.Formatters
open System.ComponentModel.DataAnnotations
open System.Globalization
open Microsoft.FSharp.Reflection
open Newtonsoft.Json.Linq
open Newtonsoft.Json.Converters
open Newtonsoft.Json.Serialization
printfn "Environment version %O" System.Environment.Version
printfn "%s version: %O" (typeof<FSharpTypeFunc>.Namespace) (typeof<FSharpTypeFunc>.Assembly.GetName())
printfn "%s version: %O" (typeof<JsonConvert>.Namespace) (typeof<JsonConvert>.Assembly.FullName)
printfn "%s version: %O" (typeof<NUnit.Framework.Assert>.Namespace) (typeof<NUnit.Framework.Assert>.Assembly.GetName())
type RecordContractResolver() =
inherit DefaultContractResolver()
let IsPrivateRecordType(t : Type) =
(not t.IsPublic && FSharpType.IsRecord(t, BindingFlags.NonPublic))
let CreateParameterizedConstructor(c : ConstructorInfo) =
ObjectConstructor<Object>(fun a -> c.Invoke(a))
override this.GetSerializableMembers(objectType : Type) =
let members = base.GetSerializableMembers(objectType)
if IsPrivateRecordType(objectType) then
let baseMembers = HashSet members
FSharpType.GetRecordFields(objectType, BindingFlags.Instance ||| BindingFlags.NonPublic)
|> Seq.filter (fun p -> p.CanRead && p.GetIndexParameters().Length = 0 && not (baseMembers.Contains(p)) && not (Attribute.IsDefined(p, typeof<JsonIgnoreAttribute>)))
override this.CreateProperty(m : MemberInfo, memberSerialization : MemberSerialization) =
let property = base.CreateProperty(m, memberSerialization)
if (IsPrivateRecordType(m.DeclaringType)
&& FSharpType.GetRecordFields(m.DeclaringType, BindingFlags.Instance ||| BindingFlags.NonPublic).Contains(m :?> PropertyInfo)) then
property.Readable <- true
override this.CreateObjectContract(objectType : Type) =
let contract = base.CreateObjectContract(objectType)
if IsPrivateRecordType(objectType) then
let constructors = objectType.GetConstructors(BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic) |> Seq.sortBy (fun c -> c.GetParameters().Length)
let c = constructors.LastOrDefault()
if not (Object.ReferenceEquals(c, null)) then
contract.OverrideCreator <- CreateParameterizedConstructor(c)
for p in (this.CreateConstructorParameters(c, contract.Properties)) do
contract.CreatorParameters.Add(p)
type Ticker = { Ask :decimal; Bid :decimal; Last: decimal; High :decimal; Timestamp :int; }
let internal t = {Ask = 0.7833m; Bid = 0.7833m; Last = 0.7833m; High = 0.7900m; Timestamp = 1010101}
let settings = new JsonSerializerSettings()
settings.ContractResolver <- new RecordContractResolver()
let json = JsonConvert.SerializeObject(t, settings)
let internal t2 = JsonConvert.DeserializeObject<models.Ticker>(json, settings)
printfn "JSON for %s is %s" (t.GetType().Name) json
[<Newtonsoft.Json.JsonProperty>] a : string
let private test = { a = "hello"; b = ["1"; "2"] }
Assert.AreEqual(test, { a = "hello"; b = ["1"; "2"] })
let testjson = JsonConvert.SerializeObject(test, settings)
let private testback = JsonConvert.DeserializeObject<test>(testjson, settings)
let testjsonback = JsonConvert.SerializeObject(testback, settings)
printfn "JSON for %s is %s" (test.GetType().Name) testjson
Assert.AreEqual(testjson, testjsonback)
Assert.AreEqual(test, testback)
[<Newtonsoft.Json.JsonObject(MemberSerialization = MemberSerialization.OptIn)>]
[<Newtonsoft.Json.JsonProperty>]
let private test2 = { Included = "hello"; NotIncluded = "Should not be present" }
let test2json = JsonConvert.SerializeObject(test2, settings)
let private test2back = JsonConvert.DeserializeObject<test2>(test2json, settings)
let test2json2 = JsonConvert.SerializeObject(test2back, settings)
printfn "JSON for %s is %s" (test2.GetType().Name) test2json
printfn "Round-trip JSON for %s is %s" (test2back.GetType().Name) test2json2
Assert.AreEqual(test2json, "{\"Included\":\"hello\"}")
Assert.AreEqual(test2json2, "{\"Included\":\"hello\"}")
Assert.AreNotEqual(test2, test2back)
Flurl.Http.FlurlHttp.Configure(fun s ->
let settings = new JsonSerializerSettings()
settings.ContractResolver <- new RecordContractResolver()
s.JsonSerializer <- NewtonsoftJsonSerializer settings