c# - 如何解决 F# 和 Azure Functions 之间的 JSON 转换冲突?
问题描述
问题:
如何解决 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 中将 F# 记录类型返回为 JSON
将 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
的@
.
这是我尝试在 .NET Core 3.1 上为 NewtonSoft.Json 12.0.3 运行的完整源代码。我看不到@
,但命名策略可能会帮助 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
推荐阅读
- data-structures - 如果在链接列表中进行无限插入会发生什么?
- reactjs - 防止重新渲染子级以供使用PDF updateInstance()
- python - ValueError:时间数据“2021-11-11 05:01:12.11111”与格式“%Y-%m-%d %H:%M:%S %p”不匹配
- python - 有谁知道为什么我没有得到输出,代码编译但不返回输出
- python - Groupby年月并在Python中查找前N个最小值列
- python - 根据另一个列表对列表进行排序
- node.js - MongoDB 环境变量错误,不确定如何继续
- caching - 如何在 arm64 上禁用 L1/L2 缓存?
- docker - 我不能在 Dockerfile 中执行 RUN cp
- python - 为什么 python 库会从桌面运行,但从 PHP 调用时却不行?