用System.Text.Json序列化记录成员



我在我的记录上使用自引用成员,如下所示:

type Payload =
{ Id: Guid }
member x.DerivedProperty = $"Derived Property using id: {x.Id}"

NewtonSoft.Json将序列化这个,但是在迁移到System.Text.Json之后,它不再包含在序列化中。

是否可以配置System.Text.Json以包含自引用成员?

编辑:这似乎是FSharp.System.Text.Json的副作用:https://github.com/Tarmil/FSharp.SystemTextJson/issues/92

寻找解决方案。请注意,如果我没有用FSharp.System.Text.Json序列化Payload,那么它将按预期序列化。

正如您在问题中指出的那样,无法序列化记录成员属性是JsonFSharpConverterFSharp.SystemTextJson的已知限制,请参阅序列化记录类型#92的成员属性:

输出不包含成员属性。删除选项中的转换器,它确实会被打印出来。

但是JsonFSharpConverter实际上对记录类型做了什么?事实证明,这个转换器是一个为f#记录类型生产JsonRecordConverter<'T>的工厂。从其源代码来看,JsonRecordConverter<'T>带来的主要优点是它总是使用其参数化构造函数构造记录。这对于。net Core 3.1中的所有记录都是必需的,对于内部记录可能仍然需要。在。net 5和更高版本中。[1]此外,还可能需要正确序列化内部记录的任何字段——我还没有测试过。<一口>[2]

因为似乎你不需要为你的Payload类型应用转换器,你可以选择不使用JsonFSharpConverter封装在一些装饰器工厂,其CanConvert(Type)方法返回false的类型,你想使用默认序列化。

例如,您可以创建一个转换器工厂,当应用某些自定义属性时,它选择不使用FSharp.System.Text.Json:

type JsonConverterFactoryDecorator (innerConverter : JsonConverterFactory) =
inherit JsonConverterFactory ()
member private this.innerConverter = match innerConverter with | null -> nullArg "innerConverter" | _ -> innerConverter // Guard against null if called from c# serialization code
override this.CanConvert(t) = innerConverter.CanConvert(t)
override this.CreateConverter(typeToConvert, options) = innerConverter.CreateConverter(typeToConvert, options)
type OptOutJsonConverterFactoryDecorator<'T when 'T :> System.Attribute> (innerConverter : JsonConverterFactory) =
inherit JsonConverterFactoryDecorator (innerConverter)
override this.CanConvert(t) = base.CanConvert(t) && not (t.IsDefined(typeof<'T>, false))
type JsonFSharpConverterOptOutAttribute () =
inherit System.Attribute()
type OptOutJsonFSharpConverter () = 
inherit OptOutJsonConverterFactoryDecorator<JsonFSharpConverterOptOutAttribute>(JsonFSharpConverter())

然后按如下方式装饰Payload:

[<JsonFSharpConverterOptOut>]
type Payload =
{ Id: Guid }
member x.DerivedProperty = "Derived Property using id: {x.Id}"

或者,如果您更愿意为所有记录选择不使用FSharp.System.Text.Json,则定义OptOutJsonFSharpConverter如下:

type OptOutJsonFSharpConverter () = 
inherit JsonConverterFactoryDecorator(JsonFSharpConverter())
override this.CanConvert(t) = base.CanConvert(t) && not (Microsoft.FSharp.Reflection.FSharpType.IsRecord(t, BindingFlags.Public ||| BindingFlags.NonPublic))

(你可能需要尝试获得最合适的记录序列化,例如,你可能需要检查t.IsPublic以重新启用JsonFSharpConverter用于内部记录)

无论您选择哪个版本的OptOutJsonFSharpConverter,都要按如下方式初始化您的选项:

let options = JsonSerializerOptions()
options.Converters.Add(OptOutJsonFSharpConverter ())

[1]参见f#内部可见性变化记录构造函数行为了解详细信息。

[2]参见内部类型的字段和方法不可能公开#2820