[英]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.