Created
December 12, 2011 21:13
-
-
Save colinbull/1469130 to your computer and use it in GitHub Desktop.
This file contains 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
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