Last active
January 3, 2016 03:29
-
-
Save eulerfx/8402409 to your computer and use it in GitHub Desktop.
Json.NET converted for serializing F# sum types.
This file contains hidden or 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
| /// For (1,"b",3) generates [1,"b",3] | |
| type TupleArrayConverter() = | |
| inherit JsonConverter() | |
| override x.CanConvert(typ) = FSharpType.IsTuple(typ) | |
| override x.WriteJson(writer, value, serializer) = | |
| let values = FSharpValue.GetTupleFields(value) | |
| serializer.Serialize(writer, values) | |
| override x.ReadJson(reader, typ, _, serializer) = | |
| let itemTypes = Util.getTupleElements typ | |
| let deserialize t = serializer.Deserialize(reader, t) | |
| let readElements() = | |
| let rec read index acc = | |
| match reader.TokenType with | |
| | JsonToken.EndArray -> acc | |
| | _ -> | |
| let value = deserialize(itemTypes.[index]) | |
| reader.Read() |> ignore | |
| read (index + 1) (value::acc) | |
| reader.Read() |> ignore | |
| read 0 List.empty | |
| match reader.TokenType with | |
| | JsonToken.StartArray -> | |
| let values = readElements() |> Array.ofList |> Array.rev | |
| FSharpValue.MakeTuple(values, typ) | |
| | _ -> failwith "invalid token" |
This file contains hidden or 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
| /// For type PingPong = Ping of int | Pong and value Ping 1 genereates "{"Ping":1}" | |
| type UnionCaseNameConverter() = | |
| inherit JsonConverter() | |
| override x.CanConvert(typ) = FSharpType.IsUnion(typ) || (typ.DeclaringType <> null && FSharpType.IsUnion(typ.DeclaringType)) | |
| override x.WriteJson(writer, value, serializer) = | |
| let typ = value.GetType() | |
| let caseInfo,fieldValues = FSharpValue.GetUnionFields(value, typ) | |
| writer.WriteStartObject() | |
| writer.WritePropertyName(caseInfo.Name) | |
| let value = | |
| match fieldValues.Length with | |
| | 0 -> null | |
| | 1 -> fieldValues.[0] | |
| | _ -> fieldValues :> obj | |
| serializer.Serialize(writer, value) | |
| writer.WriteEndObject() | |
| override x.ReadJson(reader, typ, _, serializer) = | |
| let fail() = failwith "Invalid token!" | |
| let ensure (t:JsonToken) = if reader.TokenType <> t then fail() | |
| let read (t:JsonToken) = | |
| if (reader.Read() = false || reader.TokenType <> t) then fail() | |
| else reader.Value | |
| let typ = if FSharpType.IsUnion(typ) then typ else typ.DeclaringType | |
| let cases = Util.getAndCacheUnionCases(typ) | |
| ensure JsonToken.StartObject | |
| let caseName = read JsonToken.PropertyName |> string | |
| reader.Read() |> ignore | |
| let caseInfo = cases |> Seq.find (fun c -> c.Name = caseName) | |
| let fields = caseInfo.GetFields() | |
| let caseArgs = | |
| match fields.Length with | |
| | 0 -> Array.empty | |
| | 1 -> [| serializer.Deserialize(reader, fields.[0].PropertyType) |] | |
| | _ -> | |
| let tupleType = FSharpType.MakeTupleType(fields |> Array.map (fun f -> f.PropertyType)) | |
| let tuple = serializer.Deserialize(reader, tupleType) | |
| FSharpValue.GetTupleFields(tuple) | |
| FSharpValue.MakeUnion(caseInfo, caseArgs) |
This file contains hidden or 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
| module private Util = | |
| let cache (f:'a -> 'b) = | |
| let cache = new System.Collections.Concurrent.ConcurrentDictionary<'a,'b>() | |
| fun k -> cache.GetOrAdd(k, f) | |
| let getAndCacheUnionCases = FSharpType.GetUnionCases |> cache | |
| let getTupleElements : Type -> Type[] = FSharpType.GetTupleElements |> cache |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment