繁体   English   中英

如何使用 F# 序列化/反序列化此 AWS API?

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM