Skip to content

Instantly share code, notes, and snippets.

@peterhirn
Last active April 26, 2021 19:49
Show Gist options
  • Save peterhirn/8f2151afb4951a96b38137a9279a6ef6 to your computer and use it in GitHub Desktop.
Save peterhirn/8f2151afb4951a96b38137a9279a6ef6 to your computer and use it in GitHub Desktop.
open System.Text.Json
open System.Text.Json.Serialization
open MathNet.Spatial.Euclidean
let private convertName (options : JsonSerializerOptions) : string -> string =
match options.PropertyNamingPolicy with
| null -> id
| policy -> policy.ConvertName
let private readOrFail (reader : Utf8JsonReader byref) =
if not (reader.Read()) then
raise (JsonException "Failed to read token")
let private expectProperty (reader : Utf8JsonReader byref) options name =
readOrFail &reader
if reader.TokenType <> JsonTokenType.PropertyName then
raise (JsonException $"Expected property name token, got '{reader.TokenType}'")
let propertyName = reader.GetString()
let converted = convertName options name
if propertyName <> converted then
raise (JsonException $"Expected property name '{converted}', got '{propertyName}'")
readOrFail &reader
let private expectStartObject (reader : Utf8JsonReader byref) =
if reader.TokenType <> JsonTokenType.StartObject then
raise (JsonException $"Expected start object token, got '{reader.TokenType}'")
let private expectEndObject (reader : Utf8JsonReader byref) =
readOrFail &reader
if reader.TokenType <> JsonTokenType.EndObject then
raise (JsonException $"Expected end object token, got '{reader.TokenType}'")
type Vector3DConverter() =
inherit JsonConverter<Vector3D>()
let readDouble (reader : Utf8JsonReader byref) options name =
expectProperty &reader options name
reader.GetDouble()
override _.Read(reader : Utf8JsonReader byref, _, options : JsonSerializerOptions) =
expectStartObject &reader
let x = readDouble &reader options "X"
let y = readDouble &reader options "Y"
let z = readDouble &reader options "Z"
expectEndObject &reader
Vector3D(x, y, z)
override _.Write(writer : Utf8JsonWriter, vector : Vector3D, options : JsonSerializerOptions) =
writer.WriteStartObject()
let convert = convertName options
writer.WritePropertyName (convert "X")
writer.WriteNumberValue vector.X
writer.WritePropertyName (convert "Y")
writer.WriteNumberValue vector.Y
writer.WritePropertyName (convert "Z")
writer.WriteNumberValue vector.Z
writer.WriteEndObject()
type UnitVector3DConverter() =
inherit JsonConverter<UnitVector3D>()
override _.Read(reader : Utf8JsonReader byref, _, options : JsonSerializerOptions) =
JsonSerializer.Deserialize<Vector3D>(&reader, options).ToVector() |> UnitVector3D.OfVector
override _.Write(writer : Utf8JsonWriter, vector : UnitVector3D, options : JsonSerializerOptions) =
JsonSerializer.Serialize(writer, vector.ToVector3D(), options)
type Point3DConverter() =
inherit JsonConverter<Point3D>()
override _.Read(reader : Utf8JsonReader byref, _, options : JsonSerializerOptions) =
JsonSerializer.Deserialize<Vector3D>(&reader, options).ToVector() |> Point3D.OfVector
override _.Write(writer : Utf8JsonWriter, point : Point3D, options : JsonSerializerOptions) =
JsonSerializer.Serialize(writer, point.ToVector3D(), options)
type CoordinateSystemConverter() =
inherit JsonConverter<CoordinateSystem>()
let [<Literal>] Origin = "Origin"
let [<Literal>] XAxis = "XAxis"
let [<Literal>] YAxis = "YAxis"
let [<Literal>] ZAxis = "ZAxis"
let readPoint3D (reader : Utf8JsonReader byref) options name =
expectProperty &reader options name
JsonSerializer.Deserialize<Point3D>(&reader, options)
let readVector3D (reader : Utf8JsonReader byref) options name =
expectProperty &reader options name
JsonSerializer.Deserialize<Vector3D>(&reader, options)
override _.Read(reader : Utf8JsonReader byref, _, options : JsonSerializerOptions) =
expectStartObject &reader
let origin = readPoint3D &reader options Origin
let xAxis = readVector3D &reader options XAxis
let yAxis = readVector3D &reader options YAxis
let zAxis = readVector3D &reader options ZAxis
expectEndObject &reader
CoordinateSystem(origin, xAxis, yAxis, zAxis)
override _.Write(writer : Utf8JsonWriter, system : CoordinateSystem, options : JsonSerializerOptions) =
writer.WriteStartObject()
let convert = convertName options
writer.WritePropertyName (convert Origin)
JsonSerializer.Serialize(writer, system.Origin, options)
writer.WritePropertyName (convert XAxis)
JsonSerializer.Serialize(writer, system.XAxis, options)
writer.WritePropertyName (convert YAxis)
JsonSerializer.Serialize(writer, system.YAxis, options)
writer.WritePropertyName (convert ZAxis)
JsonSerializer.Serialize(writer, system.ZAxis, options)
writer.WriteEndObject()
let options =
lazy
let fsharpConverter =
JsonFSharpConverter(
unionEncoding = (JsonUnionEncoding.ThothLike ||| JsonUnionEncoding.UnwrapOption)
)
let options = JsonSerializerOptions()
options.Converters.Add fsharpConverter
options.Converters.Add (Vector3DConverter())
options.Converters.Add (UnitVector3DConverter())
options.Converters.Add (Point3DConverter())
options.Converters.Add (CoordinateSystemConverter())
options.IgnoreNullValues <- true
options.WriteIndented <- true
options.PropertyNamingPolicy <- JsonNamingPolicy.CamelCase
options
let serialize source =
JsonSerializer.Serialize(source, options.Value)
let deserialize<'a> (source : string) : 'a =
JsonSerializer.Deserialize(source, options.Value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment