F#如何使用Newtonsoft.Json反序列化私有可辨识联合?
问题根因
.NET 5+运行时搭配较高版本的Newtonsoft.Json时,识别F#私有可辨识联合(DU)的内部构造逻辑发生了变化,仅标注[<JsonObject(MemberSerialization = MemberSerialization.Fields)>]特性无法让Json.NET找到匹配的私有构造函数。你之前设置ConstructorHandling.AllowNonPublicDefaultConstructor无效的原因是F#的单例DU本身不存在无参构造函数,自然无法匹配该规则。
解决方案
无需修改DU为类类型,只需自定义适配私有DU的JsonConverter即可实现正常反序列化,完整可用代码如下:
open Newtonsoft.Json open Newtonsoft.Json.Linq open System.Reflection open FSharp.Reflection // 通用私有单例DU序列化转换器 type PrivateSingleCaseDUConverter<'T>() = inherit JsonConverter() override _.CanConvert(objectType: System.Type) = objectType = typeof<'T> && FSharpType.IsUnion(objectType, BindingFlags.NonPublic ||| BindingFlags.Public) override _.WriteJson(writer: JsonWriter, value: obj, serializer: JsonSerializer) = serializer.Serialize(writer, value) override _.ReadJson(reader: JsonReader, objectType: System.Type, existingValue: obj, serializer: JsonSerializer) = let jObj = JObject.Load(reader) // 读取DU存储的载荷字段,默认序列化后字段名为Item let fieldValue = jObj["Item"].ToObject(typedefof<string>, serializer) // 反射获取私有DU的用例构造方法 let unionCase = FSharpType.GetUnionCases(objectType, BindingFlags.NonPublic ||| BindingFlags.Public).[0] FSharpValue.MakeUnion(unionCase, [| fieldValue |]) [<JsonObject(MemberSerialization = MemberSerialization.Fields)>] type Foo = private Bar of string let f = Bar "f" let s = JsonConvert.SerializeObject(f) // 反序列化时传入自定义转换器即可 let f2 = JsonConvert.DeserializeObject<Foo>(s, PrivateSingleCaseDUConverter<Foo>()) printf "%A" f2
如果你的项目中存在大量同类型私有单例DU,也可以将转换器注册到全局JsonSerializerSettings中,无需每次调用反序列化方法时单独传入。
内容的提问来源于stack exchange,提问作者geysernrd
相关产品推荐
相关产品推荐

