f# - 为什么 F# 可区分联合无法使其 TypeConverter 受到 JSON.NET 的尊重,而其他类型却可以?
问题描述
#r "../packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll"
type [<Struct; System.ComponentModel.TypeConverterAttribute(typeof<CC>)>] C = A of string
and CC() =
inherit System.ComponentModel.TypeConverter()
override _.CanConvertFrom (_, t) = t = typeof<string>
override _.ConvertFrom(_, _, s) = s :?> string |> A |> box<C>
override _.CanConvertTo (_, t) = t = typeof<string>
override _.ConvertTo(_, _, s, _) = s :?> C |> fun (A s) -> s |> box<string>
Newtonsoft.Json.JsonConvert.SerializeObject {|a = A "123"|}
这会导致val it : string = "{"a":{"Case":"A","Fields":["123"]}}"
,这表明TypeConverter
不受尊重。这也发生在参考 DU 上。
但是,这不会发生在JsonConverter
s 中:
#r "../packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll"
type [<Struct; Newtonsoft.Json.JsonConverter(typeof<CC>)>] C = A of string
and CC() =
inherit Newtonsoft.Json.JsonConverter()
override _.CanConvert t = t = typeof<string>
override _.ReadJson (r, _, _, _) = r.ReadAsString() |> A |> box<C>
override _.WriteJson (w, v, _) = v :?> C |> fun (A s) -> s |> w.WriteValue
Newtonsoft.Json.JsonConvert.SerializeObject {|a = A "123"|}
这导致val it : string = "{"a":"123"}"
.
将此与记录进行比较:
#r "../packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll"
type [<Struct; System.ComponentModel.TypeConverterAttribute(typeof<CC>)>] C = { A: string }
and CC() =
inherit System.ComponentModel.TypeConverter()
override _.CanConvertFrom (_, t) = t = typeof<string>
override _.ConvertFrom(_, _, s) = { A = s :?> string } |> box<C>
override _.CanConvertTo (_, t) = t = typeof<string>
override _.ConvertTo(_, _, s, _) = (s :?> C).A |> box<string>
Newtonsoft.Json.JsonConvert.SerializeObject {|a = { A = "123"}|}
这也会导致val it : string = "{"a":"123"}"
,这表明TypeConverter
受到尊重。
这表明某些东西阻止TypeConverter
了受歧视联合中的 s 被识别。原因是什么?JsonConverter
s 在字典键中不可用,所以我希望TypeConverter
s 表现更好。正确序列化上述歧视联合的可行方法是什么?
解决方案
您的问题是 Json.NET 有自己的内置转换器,用于区分联合,DiscriminatedUnionConverter
. 任何适用JsonConverter
的将始终取代已应用的TypeConverter
.
JsonConverter
可以通过在设置中或通过应用程序提供您自己的备用转换器来禁用内置转换器JsonConverterAttribute
。您已经创建了一个可以正确转换您的类型的转换器C
,但如果您希望退回到应用的类型TypeConverter
,您可以创建一个不执行任何操作并通过从andJsonConverter
返回返回到默认序列化的转换器:false
CanRead
CanWrite
type NoConverter<'a> () =
inherit JsonConverter()
override this.CanConvert(t) = (t = typedefof<'a>)
override this.CanRead = false
override this.CanWrite = false
override this.WriteJson(_, _, _) = raise (NotImplementedException());
override this.ReadJson(_, _, _, _) = raise (NotImplementedException());
然后按如下方式将其应用于您的类型(此处为演示小提琴#1 ):
type [<JsonConverterAttribute(typeof<NoConverter<C>>); System.ComponentModel.TypeConverterAttribute(typeof<CC>)>] C = A of string
and CC() =
inherit System.ComponentModel.TypeConverter()
override this.CanConvertFrom (_, t) = (t = typeof<string>)
override this.ConvertFrom(_, _, s) = s :?> string |> A |> box<C>
override this.CanConvertTo (_, t) = t = typeof<string>
override this.ConvertTo(_, _, s, _) = s :?> C |> fun (A s) -> s |> box<string>
printfn "%s" (Newtonsoft.Json.JsonConvert.SerializeObject(A "123"))
或者,在如下设置中使用它(这里是演示小提琴#2 ):
let settings = JsonSerializerSettings(Converters = [|NoConverter<C>()|])
printfn "%s" (Newtonsoft.Json.JsonConvert.SerializeObject(A "123", settings))
推荐阅读
- flutter - Flutter googleMap 方向 URL 和 streetView 未在 Android 11 中启动
- reactjs - 使用 Hooks 反应 Redux
- mysql - 如何在 MySQL 中检索两个外键 ID 和一个相同基表的数据?
- python - 如何使用 statsmodels 指定在 qq 图中绘制的 line='s' 或 line=45 线的颜色
- pointers - 引用的静态变量中的错误值
- sql - 合并表时如何在2列中使用1个字段
- algorithm - 现代搜索引擎如何合并倒排索引中的大型帖子列表
- python - Zoho CRM API:基于 Python 请求的 POST 或 GET 身份验证 + 联系人插入
- python - 为什么代码给我错误的输出金额?(修改问题)
- python - 调用 main 函数后,Discord.py 命令将不起作用