Skip to content

Instantly share code, notes, and snippets.

@colinbull
Created December 12, 2011 21:13
Show Gist options
  • Save colinbull/1469130 to your computer and use it in GitHub Desktop.
Save colinbull/1469130 to your computer and use it in GitHub Desktop.
type UnionTypeConverter() =
inherit JsonConverter()
let doRead pos (reader: JsonReader) =
reader.Read() |> ignore //pop start obj type label
// printfn "%sRead %s %A %A" ("".PadLeft(reader.Depth)) pos reader.Value reader.TokenType
override x.CanConvert(typ:Type) =
let result =
((typ.GetInterface(typeof<System.Collections.IEnumerable>.FullName) = null)
&& FSharpType.IsUnion typ)
result
override x.WriteJson(writer: JsonWriter, value: obj, serializer: JsonSerializer) =
let t = value.GetType()
let write (name : string) (fields : obj []) =
writer.WriteStartObject()
writer.WritePropertyName("case")
writer.WriteValue(name)
writer.WritePropertyName("values")
serializer.Serialize(writer, fields)
writer.WriteEndObject()
let (info, fields) = FSharpValue.GetUnionFields(value, t)
write info.Name fields
override x.ReadJson(reader: JsonReader, objectType: Type, existingValue: obj, serializer: JsonSerializer) =
let cases = FSharpType.GetUnionCases(objectType)
if reader.TokenType <> JsonToken.Null
then
// printfn "%A %A" objectType.FullName existingValue
doRead "1" reader
doRead "2" reader
//printfn "Cases %A %A %A" cases reader.TokenType reader.Value
let case = cases |> Array.find(fun x -> x.Name = if reader.Value = null then "None" else reader.Value.ToString())
doRead "3" reader
doRead "4" reader
doRead "5" reader //pop item name
let fields = [|
for field in case.GetFields() do
let result = serializer.Deserialize(reader, field.PropertyType)
reader.Read() |> ignore
yield result
|]
let result = FSharpValue.MakeUnion(case, fields)
while reader.TokenType <> JsonToken.EndObject do
doRead "6" reader
result
else
FSharpValue.MakeUnion(cases.[0], [||])
//Example usage
open Newtonsoft.Json
type HtmlElement =
| Value of string option
| Node of string * HtmlElement
let jsonSettings =
let js = new JsonSerializerSettings()
js.Converters.Add(new UnionTypeConverter())
js
let x = Node("div", Node("a", Value(None)))
let serialisedValue = JsonConvert.SerializeObject(x, Formatting.Indented, jsonSettings)
let y = JsonConvert.DeserializeObject<HtmlElement>(serialisedValue, jsonSettings)
(* Produces this json *)
"{
"case": "Node",
"values": [
"div",
{
"case": "Node",
"values": [
"a",
{
"case": "Value",
"values": [
null
]
}
]
}
]
}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment