为啥 F# 可区分联合无法使其 TypeConverter 受到 JSON.NET 的尊重,而其他类型却可以?
Posted
技术标签:
【中文标题】为啥 F# 可区分联合无法使其 TypeConverter 受到 JSON.NET 的尊重,而其他类型却可以?【英文标题】:Why does an F# Discriminated Union fails to have its TypeConverter respected by JSON.NET but other types can?为什么 F# 可区分联合无法使其 TypeConverter 受到 JSON.NET 的尊重,而其他类型却可以? 【发布时间】:2020-04-04 15:41:08 【问题描述】:#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 表现更好。正确序列化上述歧视联合的可行方法是什么?
【问题讨论】:
我想我知道你的问题是什么,但我无法编译你的代码。见dotnetfiddle.net/hWmNcz。你能请edit你的问题来修复编译吗?以下是否正确修复了编译? dotnetfiddle.net/lQ4Ouf。 minimal reproducible example 会很棒。 【参考方案1】:您的问题是 Json.NET 有自己的内置转换器,用于区分联合,DiscriminatedUnionConverter
。任何适用的JsonConverter
将始终取代已应用的TypeConverter
。
可以通过在settings 中或通过应用的JsonConverterAttribute
提供您自己的备用JsonConverter
来禁用内置转换器。您已经创建了一个转换器,可以正确转换您的类型C
,但如果您希望退回到应用的TypeConverter
,您可以创建一个不执行任何操作并通过返回@987654336 退回到默认序列化的JsonConverter
@来自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 here):
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 here):
let settings = JsonSerializerSettings(Converters = [|NoConverter<C>()|])
printfn "%s" (Newtonsoft.Json.JsonConvert.SerializeObject(A "123", settings))
【讨论】:
以上是关于为啥 F# 可区分联合无法使其 TypeConverter 受到 JSON.NET 的尊重,而其他类型却可以?的主要内容,如果未能解决你的问题,请参考以下文章