Created
April 14, 2014 04:40
-
-
Save mausch/10616448 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 Address = { | |
Name: string | |
Address1: string option | |
Address2: string option | |
Code: string option | |
City: string option | |
Country: string option | |
} | |
type Client = { | |
Name: string | |
Address: Address option | |
Email: string option | |
} | |
type InvoiceRow = { | |
Title: string | |
Quantity: int option | |
QuantityUnit: string option | |
Price: decimal option | |
Tax: decimal option | |
TaxPrice: decimal option | |
TotalTaxPrice: decimal option | |
TotalPrice: decimal option | |
} | |
type Invoice = { | |
Title: string | |
Client: Client option | |
Address: Address option | |
InvoiceRows: InvoiceRow list | |
} | |
open System | |
open Fleece // https://www.nuget.org/packages/Fleece/ | |
open Fleece.Operators | |
open FSharpPlus | |
type Address with | |
static member FromJSON (_: Address) = | |
function | |
| JObject o -> | |
monad { | |
let! name = o .@ "name" | |
let! address1 = o .@? "address_1" | |
let! address2 = o .@? "address_2" | |
let! code = o .@? "code" | |
let! city = o .@? "city" | |
let! country = o .@? "country" | |
return { | |
Address.Name = name | |
Address1 = address1 | |
Address2 = address2 | |
Code = code | |
City = city | |
Country = country | |
} | |
} | |
| x -> Failure (sprintf "Expected JSON object for Address, found %A" x) | |
let email address = | |
try | |
System.Net.Mail.MailAddress address |> ignore | |
Success address | |
with _ -> Failure (sprintf "Invalid email address '%s'" address) | |
let optValidate (f: 'a -> Choice<'a, _>) = | |
function | |
| None -> Success None | |
| Some v -> f v |> map Some | |
type Client with | |
static member FromJSON (_: Client) = | |
function | |
| JObject o -> | |
monad { | |
let! name = o .@ "name" | |
let! address = o .@? "address" | |
let! email = o .@? "email" >>= optValidate email | |
return { | |
Client.Name = name | |
Address = address | |
Email = email | |
} | |
} | |
| x -> Failure (sprintf "Expected JSON object for Client, found %A" x) | |
let min minimumValue value = | |
if value >= minimumValue | |
then Success value | |
else Failure (sprintf "Expected minimum value %A, found %A" minimumValue value) | |
type InvoiceRow with | |
static member FromJSON (_: InvoiceRow) = | |
function | |
| JObject o -> | |
monad { | |
let! title = o .@ "title" | |
let! quantity = o .@? "quantity" >>= optValidate (min 0) | |
let! quantityUnit = o .@? "quantityUnit" | |
let! price = o .@? "price" | |
let! tax = o .@? "tax" >>= optValidate (min 0m) | |
let! taxPrice = o .@? "taxPrice" >>= optValidate (min 0m) | |
let! totalTaxPrice = o .@? "totalTaxPrice" | |
let! totalPrice = o .@? "totalPrice" | |
return { | |
InvoiceRow.Title = title | |
Quantity = quantity | |
QuantityUnit = quantityUnit | |
Price = price | |
Tax = tax | |
TaxPrice = taxPrice | |
TotalTaxPrice = totalTaxPrice | |
TotalPrice = totalPrice | |
} | |
} | |
| x -> Failure (sprintf "Expected JSON object for InvoiceRow, found %A" x) | |
// dummy implementation | |
let foreignKeyInCollection table key = Success { Client.Name = "John Doe"; Address = None; Email = None } | |
type Invoice with | |
static member FromJSON (_: Invoice) = | |
function | |
| JObject o -> | |
monad { | |
let! title = o .@ "title" | |
let! client = o .@? "client" >>= optValidate (foreignKeyInCollection "client") | |
let! address = o .@? "address" | |
let! invoiceRows = o .@? "invoiceRows" |> map (fun l -> defaultArg l []) | |
return { | |
Invoice.Title = title | |
Client = client | |
Address = address | |
InvoiceRows = invoiceRows | |
} | |
} | |
| x -> Failure (sprintf "Expected JSON object for Invoice, found %A" x) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment