Skip to content

Instantly share code, notes, and snippets.

@mjul
Created April 5, 2016 15:13
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjul/4bff61662ed2e60b89cd1533c9fcaba6 to your computer and use it in GitHub Desktop.
Save mjul/4bff61662ed2e60b89cd1533c9fcaba6 to your computer and use it in GitHub Desktop.
F# Chiron JSON serialization experiments
#I "./packages"
#r "FParsec/lib/net40-client/FParsec.dll"
#r "FParsec/lib/net40-client/FParsecCS.dll"
#r "Aether/lib/net35/Aether.dll"
#r "Chiron/lib/net40/Chiron.dll"
#r "System.Runtime.Serialization"
open Chiron
// Serialization works well with static members
// as the default serializer uses the ToJson and FromJson
// member functions
//
// This is from the article by Marcus Griep:
// Chiron Compuation Expressions
// https://neoeinstein.github.io//blog/2016/04-02-chiron-computation-expressions/index.html
type User =
{ Name: string; IsAdmin: bool }
static member ToJson (x:User) =
json {
do! Json.write "name" x.Name
do! Json.write "isAdmin" x.IsAdmin
}
static member FromJson (_:User) =
json {
let! n = Json.read "name"
let! a = Json.read "isAdmin"
return {Name=n; IsAdmin=a}
}
Json.serialize {Name="Martin";IsAdmin=true}
|> Json.formatWith Chiron.Formatting.JsonFormattingOptions.Pretty
|> printfn "%s"
//
// Let's turn op the volume
//
// Define the internal data model: investment funds in categories
// Note that the model does not know about JSON formats
type Category = {Id: int; Name: string}
type InvestmentFund = { Id: int; Name: string; AnnualCostInPercent:decimal; Isin: string; Category: Category}
// Some test data
let loadAllProducts () =
let danishStocks = {Category.Id=1; Name="Danish Stocks"}
let globalStocks = {Category.Id=2; Name="Global Stocks"}
let categories = [danishStocks; globalStocks]
let products =
[
{Id=1; Name="Golddigger Danish"; AnnualCostInPercent=1.25m; Isin="DK11111111"; Category=danishStocks}
{Id=2; Name="Starmanager Danish"; AnnualCostInPercent=1.75m; Isin="DK22222222"; Category=danishStocks}
{Id=3; Name="Thrifty Danish"; AnnualCostInPercent=0.55m; Isin="DK33333333"; Category=globalStocks}
{Id=4; Name="Golden Globals"; AnnualCostInPercent=2.25m; Isin="DK44444444"; Category=globalStocks}
{Id=5; Name="Thrifty Globals"; AnnualCostInPercent=0.55m; Isin="DK55555555"; Category=globalStocks}
]
(categories, products)
// We can use the "json" computation expression
// to handle serialization and deserialization
//
// This way, the serialization is not conflated into the data type
//
// The trick is to use the serializeWith and readWith functions
// and provide the serializers directly
let serializeCategory (x:Category) =
json {
do! Json.write "_type" "Category"
do! Json.write "id" x.Id
do! Json.write "name" x.Name
}
let deserializeCategory =
json {
let! (t:string) = Json.read "_type"
let! id = Json.read "id"
let! name = Json.read "name"
return {Category.Id = id; Name=name}
}
let someCategory = loadAllProducts () |> fst |> Seq.head
// Try serializing with the non-member functions
let serialized =
someCategory
|> Json.serializeWith serializeCategory
|> Json.formatWith JsonFormattingOptions.Pretty
// That was easy
// Unfortunately deserialization is not as easy as since there
// Json.deserialize requires a member function on the type for
// deserialization. It does not offer a Json.deserializeWith
// either.
//
// Anyway, we can deserialize it like this
// (this is a bit clumsy - I wonder if there is a better way)
//
let dc = serialized |> Json.parse |> deserializeCategory
let deserialized =
(dc) |> function
| Value (c:Category), _ -> Some c
| Error e, _ -> None
printfn "Serialized:%s%s" System.Environment.NewLine serialized
printfn "Deserialized:%s%A" System.Environment.NewLine deserialized
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment