F#, Json, 选项类型的 WebApi 序列化
F#, Json, WebApi Serialization of Option Types
我在向 C# WebApi 项目公开的 F# 项目中使用记录类型。例如:
type Account = {Amount:float; Number:int; Holder:string}
基于 this post and this post,json 正在正确序列化。
{"Amount":100.0,"Number":1,"Holder":"Homer"}
但是,当我在记录中添加选项类型时,
type Account = {Amount:float; Number:int; Holder:string option }
json 变得脱胶。
{"Amount":100.0,"Number":1,"Holder":{"Case":"Some","Fields":["Homer"]}}
我希望 json 看起来与非选项类型记录相同,序列化程序足够智能,可以获取值并自动将它们放入 in/out 选项类型。
有人为此构建了自定义格式化程序吗?有什么我遗漏的 OOB 吗?
谢谢
处理选项类型和单一类型区分联合的自定义 Json.NET 转换器确实存在(或者至少声称,我只测试了选项类型的情况)。可以查到here.
用法:
let act = {Amount= 100.0; Number= 1; Holder= Some "Homer"}
let json = JsonConvert.SerializeObject(act, new IdiomaticDuConverter())
我尝试了另一个答案中链接的转换器,但我不喜欢其他不是 Option 的 DU 的输出。相反,您可能希望选择仅更改选项类型的行为而不是所有 DU。
我发现此转换器只会更改选项类型的行为以在 None 选项上呈现 null,否则更改值。可以找到原始 code/author 信息 here。
open System
open Microsoft.FSharp.Reflection
open Newtonsoft.Json
open Newtonsoft.Json.Converters
type OptionConverter() =
inherit JsonConverter()
override x.CanConvert(t) =
t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
override x.WriteJson(writer, value, serializer) =
let value =
if value = null then null
else
let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
fields.[0]
serializer.Serialize(writer, value)
override x.ReadJson(reader, t, existingValue, serializer) =
let innerType = t.GetGenericArguments().[0]
let innerType =
if innerType.IsValueType then (typedefof<Nullable<_>>).MakeGenericType([|innerType|])
else innerType
let value = serializer.Deserialize(reader, innerType)
let cases = FSharpType.GetUnionCases(t)
if value = null then FSharpValue.MakeUnion(cases.[0], [||])
else FSharpValue.MakeUnion(cases.[1], [|value|])
使用转换器与其他答案相同:
let json = JsonConvert.SerializeObject(myObj, new OptionConverter())
我在向 C# WebApi 项目公开的 F# 项目中使用记录类型。例如:
type Account = {Amount:float; Number:int; Holder:string}
基于 this post and this post,json 正在正确序列化。
{"Amount":100.0,"Number":1,"Holder":"Homer"}
但是,当我在记录中添加选项类型时,
type Account = {Amount:float; Number:int; Holder:string option }
json 变得脱胶。
{"Amount":100.0,"Number":1,"Holder":{"Case":"Some","Fields":["Homer"]}}
我希望 json 看起来与非选项类型记录相同,序列化程序足够智能,可以获取值并自动将它们放入 in/out 选项类型。
有人为此构建了自定义格式化程序吗?有什么我遗漏的 OOB 吗?
谢谢
处理选项类型和单一类型区分联合的自定义 Json.NET 转换器确实存在(或者至少声称,我只测试了选项类型的情况)。可以查到here.
用法:
let act = {Amount= 100.0; Number= 1; Holder= Some "Homer"}
let json = JsonConvert.SerializeObject(act, new IdiomaticDuConverter())
我尝试了另一个答案中链接的转换器,但我不喜欢其他不是 Option 的 DU 的输出。相反,您可能希望选择仅更改选项类型的行为而不是所有 DU。
我发现此转换器只会更改选项类型的行为以在 None 选项上呈现 null,否则更改值。可以找到原始 code/author 信息 here。
open System
open Microsoft.FSharp.Reflection
open Newtonsoft.Json
open Newtonsoft.Json.Converters
type OptionConverter() =
inherit JsonConverter()
override x.CanConvert(t) =
t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
override x.WriteJson(writer, value, serializer) =
let value =
if value = null then null
else
let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
fields.[0]
serializer.Serialize(writer, value)
override x.ReadJson(reader, t, existingValue, serializer) =
let innerType = t.GetGenericArguments().[0]
let innerType =
if innerType.IsValueType then (typedefof<Nullable<_>>).MakeGenericType([|innerType|])
else innerType
let value = serializer.Deserialize(reader, innerType)
let cases = FSharpType.GetUnionCases(t)
if value = null then FSharpValue.MakeUnion(cases.[0], [||])
else FSharpValue.MakeUnion(cases.[1], [|value|])
使用转换器与其他答案相同:
let json = JsonConvert.SerializeObject(myObj, new OptionConverter())