Reputation: 8562
I have a few types in F# like:
type ResourceRecordSet =
| Alias of Name : string *
Type : ResourceRecordType *
AliasTarget : AliasTarget
| Record of Name : string *
Type : ResourceRecordType *
ResourceRecords : List<string> * TTL : uint32
Using the type:
let r =
Record(
"domain.tld."
, SOA
, ["ns-583.awsdns-08.net.
awsdns-hostmaster.amazon.com.
1 7200 900 1209600 86400"]
, 900u
)
When I try to serialize it to JSON I get the following:
let rjson = JsonSerializer.Serialize(r)
sprintf "%A" rjson
Output:
"{"Case":"Record","Fields":["doman.tld.",{"Case":"SOA"},["ns-583.awsdns-08.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"],900]}"
Is there a way to control the serialization and produce the following instead:
{
"Name": "doman.tld.",
"ResourceRecords": [ {"Value": "ns-583.awsdns-08.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400" }],
"TTL": 900,
"Type": "SOA"
}
Upvotes: 2
Views: 694
Reputation: 8562
To answer my own question, after reading up on the different libraries suggested by different people, Fleece seems like the most solid solution here.
First a simple example:
open System.Text.Json
open Fleece.SystemTextJson
open Fleece.SystemTextJson.Operators
open FSharpPlus
type AliasTarget =
{
DNSName : string
EvaluateTargetHealth : bool
HostedZoneId : string
}
static member ToJson (a: AliasTarget) =
jobj [
"DNSName" .= a.DNSName
"EvaluateTargetHealth" .= a.EvaluateTargetHealth
"HostedZoneId" .= a.HostedZoneId
]
static member OfJson json =
match json with
| JObject o ->
monad {
let! dnsName = o .@ "DNSName"
let! evaluateTargetHealth = o .@ "EvaluateTargetHealth"
let! hostedZoneId = o .@ "HostedZoneId"
return {
DNSName = dnsName
EvaluateTargetHealth = evaluateTargetHealth
HostedZoneId = hostedZoneId
}
}
| x -> Decode.Fail.objExpected x
let outp = aliasTargetToJSON { DNSName = "dbrgct5gwrbsd.cloudfront.net."; EvaluateTargetHealth = false; HostedZoneId = "xxx"}
loggerBlog.LogInfo outp
let aliasJson = """{"DNSName":"dbrgct5gwrbsd.cloudfront.net.","EvaluateTargetHealth":false,"HostedZoneId":"xxx"}"""
let alias : AliasTarget ParseResult = parseJson aliasJson
loggerBlog.LogInfo (sprintf "%A" alias)
This prints:
2020-06-08T23:26:09 INFO [Website] {"DNSName":"dbrgct5gwrbsd.cloudfront.net.","EvaluateTargetHealth":false,"HostedZoneId":"xxx"}
2020-06-08T23:26:09 INFO [Website] Ok { DNSName = "dbrgct5gwrbsd.cloudfront.net."
EvaluateTargetHealth = false
HostedZoneId = "xxx" }
Both the serialization and deserialization works.
ADTs or discriminated unions can be implemented as well:
type Shape =
| Rectangle of width : float * length : float
| Circle of radius : float
| Prism of width : float * float * height : float
with
static member JsonObjCodec =
Rectangle <!> jreq "rectangle" (function Rectangle (x, y) -> Some (x, y) | _ -> None)
<|> ( Circle <!> jreq "radius" (function Circle x -> Some x | _ -> None) )
<|> ( Prism <!> jreq "prism" (function Prism (x, y, z) -> Some (x, y, z) | _ -> None) )
More here:
https://github.com/fsprojects/Fleece
Upvotes: 1