[英]How to serialize / deserialize this AWS API with F#?
對於 DNS 記錄,AWS 具有以下 API:
{
"ResourceRecordSets": [
{
"Name": "domain.tld.",
"ResourceRecords": [
{
"Value": "ns-583.awsdns-08.net."
},
{
"Value": "ns-1176.awsdns-19.org."
}
],
"TTL": 172800,
"Type": "NS"
},
{
"AliasTarget": {
"DNSName": "hello.cloudfront.net.",
"EvaluateTargetHealth": false,
"HostedZoneId": "XYZ2123"
},
"Name": "some.domain.tld.",
"Type": "A"
}
]
}
我試圖用 Fleece 對其進行序列化/反序列化,這對於 JSON 來說是一個了不起的工具。
type AliasTarget = {
DNSName : string
EvaluateTargetHealth : bool
HostedZoneId : string
}
with
static member JsonObjCodec =
fun d e h -> { DNSName = d; EvaluateTargetHealth = e; HostedZoneId = h }
|> withFields
|> jfield "DNSName" (fun d -> d.DNSName)
|> jfield "EvaluateTargetHealth" (fun e -> e.EvaluateTargetHealth)
|> jfield "HostedZoneId" (fun h -> h.HostedZoneId)
type ResourceRecord =
{ Value : string }
with
static member JsonObjCodec =
fun r -> { Value = r }
|> withFields
|> jfield "Value" (fun r -> r.Value)
type ResourceRecords =
List<ResourceRecord>
type ResourceRecordsOrAlias =
| AliasTarget of
AliasTarget
| ResourceRecords of
ResourceRecords
with
static member JsonObjCodec =
(
( AliasTarget <!> jreq "AliasTarget" (function AliasTarget a -> Some a | _ -> None) )
<|>
( ResourceRecords <!> jreq "ResourceRecords" (function ResourceRecords r -> Some r | _ -> None) )
)
type ResourceRecordSet =
{
Name : string
RecordsOrAlias : ResourceRecordsOrAlias
TTL : Option<uint32>
Type : string
}
with
static member JsonObjCodec =
fun n r tt ty -> { Name = n; RecordsOrAlias = r; TTL =tt; Type = ty}
|> withFields
|> jfield "Name" (fun n -> n.Name)
// error
|> jchoice [
AliasTarget <!> jfield "AliasTarget" (fun r -> r.RecordsOrAlias)
ResourceRecords <!> jfield "ResourceRecords" (fun r -> r.RecordsOrAlias)
]
|> jfieldOpt "TTL" (fun tt -> tt.TTL)
|> jfield "Type" (fun ty -> ty.Type)
看來我無法在最終編解碼器中分支。 我嘗試了幾種不同的選擇。 有沒有辦法
我假設您使用的是最新版本(目前),即 Fleece 的 0.8。
我看到的第一個問題是您混合使用相同的方法,運算符方法( jchoice
屬於它)和詳細方法。 在不同的方法中使用不同的 styles 是可以的,但不能在同一個方法上使用。
目前這很棘手,因為歷史原因他們使用不同的類型,所以不能像這樣混合。
然而,一旦你把它整理出來,它就會把你帶到下一個問題:你不能重復使用 DU,因為使用普通的jreq {tagname}
將創建一個包含 DU 字段的標簽,但是查看json 樣品不是您需要的。 您需要直接插入它,而無需創建新標簽。
我可以向您推薦這個 function 來做到這一點:
let jfrom codec f = Codec.compose (Codec.ofConcrete codec) (Ok, f) |> Codec.toConcrete
(並且也會向 Fleece 提出建議)。
這個 function 在編碼器的輸入上映射(或更准確地說是反映射)。
在您的情況下,您將在ResourceRecordSet
的編碼器輸入上使用此 function 到 map ,以便從ResourceRecordsOrAlias
進行編碼。
因此,您的方法將是:
static member JsonObjCodec =
fun n r tt ty -> { Name = n; RecordsOrAlias = r; TTL =tt; Type = ty}
<!> jreq "Name" (fun n -> Some n.Name)
<*> jfrom ResourceRecordsOrAlias.JsonObjCodec (fun r -> r.RecordsOrAlias)
<*> jopt "TTL" (fun tt -> tt.TTL)
<*> jreq "Type" (fun ty -> Some ty.Type)
您是否考慮過使用 JSON 類型的提供程序?
https://fsharp.github.io/FSharp.Data/library/JsonProvider.html
您的樣品
[<Literal>]
let amazonSample = """
{
"ResourceRecordSets": [
{
"Name": "domain.tld.",
"ResourceRecords": [
{
"Value": "ns-583.awsdns-08.net."
},
{
"Value": "ns-1176.awsdns-19.org."
}
],
"TTL": 172800,
"Type": "NS"
},
{
"AliasTarget": {
"DNSName": "hello.cloudfront.net.",
"EvaluateTargetHealth": false,
"HostedZoneId": "XYZ2123"
},
"Name": "some.domain.tld.",
"Type": "A"
}
]
}
"""
聲明類型提供者
type Amazon = JsonProvider<amazonSample,RootName="ResourceRecordSets">
反序列化
let r = Amazon.Parse(amazonSample)
printfn "%s" r.ResourceRecordSets.[1].AliasTarget.Value.DnsName
序列化
let alias = new Amazon.AliasTarget( "hello.cloudfront.net.",false,"XYZ2123")
let res1 = new Amazon.ResourceRecord( "ns-583.awsdns-08.net.")
let record1 = new Amazon.ResourceRecordSet2( "domain.tld.", [|res1|], Some(342), "NS", Some(alias))
let recordSet = new Amazon.ResourceRecordSet( [|record1|] )
printfn "%s" (recordSet.JsonValue.ToString())
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.