簡體   English   中英

如何解決 F# 和 Azure Functions 之間的 JSON 轉換沖突?

[英]How do I resolve JSON conversion conflicts between F# and Azure Functions?

問題:

如何解決 F# 和 Azure Functions 之間的 JSON 轉換沖突?

研究:

我已經回顧了幾個相關的問題。 然而,他們傾向於專注於返回響應而不是發送請求。

此外,我真的不想裝飾我在所有客戶端和后端服務中擁有的每種記錄類型中的每個成員

F# 客戶端:

我想我可以在客戶端上使用合同解析器來解決 JSON 沖突。 然而,我失敗了。

let postTo (baseAddress:string) (resource:string) (payload:Object) =

    let settings = JsonSerializerSettings()
    settings.ContractResolver <- new DefaultContractResolver()

    let json = JsonConvert.SerializeObject(payload, settings);

    let encodedUrl = getAddress baseAddress resource
    let result     = GlobalHttpClient.Instance.PostAsJsonAsync(encodedUrl, json) |> toResult
    result

C#服務器:

然后我想我可以在服務器上使用合同解析器來解決 JSON 沖突。 然而,我還是失敗了。

此外,我在嘗試反序列化時遇到異常。 它抱怨它無法將字符串轉換為我的記錄類型之一。

public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    ILogger log,
    ExecutionContext context)
{
    try
    {
        using (var streamReader = new StreamReader(req.Body))
        {
            var requestBody = await streamReader.ReadToEndAsync();

            var settings = new JsonSerializerSettings();
            settings.ContractResolver = new DefaultContractResolver();

            var data = JsonConvert.DeserializeObject<DeviceOrigin>(requestBody, settings); // Exception thrown here ! ! !

            ...
        }
    }

}

錯誤:

無法從 System.String 強制轉換或轉換為 Courier.Account.Language+DeviceOrigin。

不需要的分辨率:

正如前面提到,修飾記錄類型的屬性可以解決這個問題,但是對於我所有解決方案中使用的每個數據傳輸結構來說都非常乏味。

如果我在客戶端和服務器上裝飾類型,則以下內容有效:

[<DataContract>]
type DeviceOrigin = {

    [<field: DataMember(Name="DeviceId")>]
    DeviceId   : string

    [<field: DataMember(Name="Coordinate")>]
    Coordinate : Coordinate
}

[<DataContract>]
type Applicant = {

    [<field: DataMember(Name="ApplicantId")>]
    ApplicantId : string

    [<field: DataMember(Name="FirstName")>]
    FirstName   : string

    [<field: DataMember(Name="LastName")>]
    LastName    : string

    [<field: DataMember(Name="Phone")>]
    Phone       : string

    [<field: DataMember(Name="Email")>]
    Email       : string

    [<field: DataMember(Name="Origin")>]
    Origin      : Coordinate
}

附錄:

在 Azure Functions 中以 JSON 形式返回 F# 記錄類型

將 F# 記錄類型序列化為 JSON 包括每個屬性后的“@”字符

{"ApplicantId@":"","FirstName@":"John","LastName@":"Doe","Phone@":"555.555.5555","Email@":"j.doe@abc.com","Origin@":{"Latitude@":37.421998333333335,"Longitude@":-122.08400000000002}}

我不確定問題究竟是什么,因為該問題缺少一個最小的可重復示例,但是查看其他問題,似乎由 F# 附加在字段成員上的@會導致某些問題。 我嘗試在我的機器上重現它,但 NewtonSoft.Json 沒有為我附加@ 這讓我認為 OP 正在客戶端和服務器上運行不同版本的 .net 和/或 NewtonSoft.Json,這導致了問題。

檢查客戶端和服務器是否運行相同版本的 NewtonSoft.Json 並且最好是最新版本可能是個好主意。

可以提供幫助的是提供您自己的NamingStrategy ,這允許您覆蓋序列化為 Json 的名稱,可能讓您去除@

這是我嘗試在 .NET Core 3.1 上為 NewtonSoft.Json 12.0.3 運行的完整源代碼。 我沒有看到@但可能 NamingStrategy 可以幫助 OP 走得更遠。

open Newtonsoft.Json
open Newtonsoft.Json.Serialization

[<CLIMutable>]
type Coordinate = 
  {
    Latitude    : float
    Longitude   : float
  }

[<CLIMutable>]
type Applicant = 
  {
    ApplicantId : string
    FirstName   : string
    LastName    : string
    Phone       : string
    Email       : string
    Origin      : Coordinate
  }
let applicant = 
  { 
    ApplicantId = "a_0001"
    FirstName   = "Hello"
    LastName    = "There"
    Phone       = "+1 555-2345"
    Email       = "a@b.com"
    Origin      = { Latitude = 23.45; Longitude = 65.43 }
  }

let jsonSerializerSettings = 
  let settings = JsonSerializerSettings ()
  let contractResolver = DefaultContractResolver ()
  // One can override the naming strategy, below prints input and output for SnakeCaseNamingStrategy
  //  One should be able to strip out the @ by providing a custom naming strategy
  let namingStrategy = 
    { new SnakeCaseNamingStrategy() with
        override x.GetDictionaryKey key =
          let key' = base.GetDictionaryKey key
          printfn "GetDictionaryKey: %A -> %A" key key'
          key'
        override x.GetExtensionDataName name = 
          let name' = base.GetExtensionDataName name
          printfn "GetExtensionDataName: %A -> %A" name name'
          name'
        override x.GetPropertyName (name, flag) = 
          let name' = base.GetPropertyName (name, flag)
          printfn "GetPropertyName: (%A, %A) -> %A" name flag name'
          name'
        override x.ResolvePropertyName name = 
          let name' = base.ResolvePropertyName name
          printfn "ResolvePropertyName: %A -> %A" name name'
          name'
    }
  contractResolver.NamingStrategy <- namingStrategy
  settings.ContractResolver <- contractResolver
  settings

let readJson (json: string): Applicant =
  JsonConvert.DeserializeObject<Applicant> (json, jsonSerializerSettings)

let writeJson (applicant: Applicant): string =
  JsonConvert.SerializeObject (applicant, jsonSerializerSettings)

[<EntryPoint>]
let main argv =
  let json = writeJson  applicant
  let app  = readJson   json

  printfn "E: %A" applicant
  printfn "J: %A" json
  printfn "A: %A" app
  0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM