簡體   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