IgnoreMissingMember 设置似乎不适用于 FSharpLu.Json 解串器

IgnoreMissingMember setting doesn't seem to work with FSharpLu.Json deserializer

这是以下内容:

我正在使用 FSharpLu.Json 反序列化一些具有额外未绑定 属性 的 JSON。这是代码:

open System
open Newtonsoft.Json
open Microsoft.FSharpLu.Json

type r =
    {
        a: int
    }

let a =
    "{\"a\":3, \"b\":5}" 

Compact.TupleAsArraySettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Compact.deserialize<r> a  // doesn't work

尽管设置 MissingMemberHandling.Ignore 它 returns 一个 json.net 错误:

Could not find member 'b' on object of type 'r'. Path 'b', line 1, position 13.

有没有办法让它工作,还是 FSharpLu.Json 有问题?

这是fiddle:https://dotnetfiddle.net/OsVv1M

附带说明一下,FSharpLu.Json 中还有另一个反序列化器,我可以使用该代码进行处理:

FSharpLu.Json.Default.Internal.DefaultSettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Default.deserialize<r> a

会工作,但反序列化器不处理受歧视的联合...所以我需要让紧凑的工作。

在查看 FSharpLu.Json 的来源时,我发现了这个:

/// Compact serialization where tuples are serialized as heterogeneous arrays
type TupleAsArraySettings =
    static member formatting = Formatting.Indented
    static member settings =
        JsonSerializerSettings(
            NullValueHandling = NullValueHandling.Ignore,

            // MissingMemberHandling is not technically needed for
            // compact serialization but it avoids certain ambiguities
            // that guarantee that deserialization coincides with the
            // default Json.Net deserialization.
            // (where 'coincides' means 'if the deserialization succeeds they both return the same object')
            // This allows us to easily define the BackwardCompatible
            // serializer (that handles both Compact and Default Json format) by reusing
            // the Compact deserializer.
            MissingMemberHandling = MissingMemberHandling.Error,
            Converters = [| CompactUnionJsonConverter(true, true) |]
        )

所以他们明确地将 MissingMemberHandling 设置为 Error;也许解决方案是实例化解串器,更改设置然后使用它。

您尝试改变的序列化程序设置 Compact.TupleAsArraySettings.settings 是一个 静态成员 ,如 code:[=35= 中所示]

type TupleAsArraySettings =
    static member formatting = Formatting.Indented
    static member settings =
        JsonSerializerSettings(
            NullValueHandling = NullValueHandling.Ignore,

            // MissingMemberHandling is not technically needed for
            // compact serialization but it avoids certain ambiguities
            // that guarantee that deserialization coincides with the
            // default Json.Net deserialization.
            // (where 'coincides' means 'if the deserialization succeeds they both return the same object')
            // This allows us to easily define the BackwardCompatible
            // serializer (that handles both Compact and Default Json format) by reusing
            // the Compact deserializer.
            MissingMemberHandling = MissingMemberHandling.Error,
            Converters = [| CompactUnionJsonConverter(true, true) |]
        )

由于 member 实际上是一个 成员函数(即方法),如 F# for fun and profit: Attaching functions to types 所述, settings 实际上(在 c# 术语中)是一个静态的 属性,每次调用时都会返回 JsonSerializerSettings 的新实例。为了测试这一点,我们可以这样做:

printfn "%b" (Object.ReferenceEquals(Compact.TupleAsArraySettings.settings, Compact.TupleAsArraySettings.settings)) // prints "false"

打印 "false"。因此改变返回值对 Compact 的行为没有影响。 c# 意义上的静态字段将由 static let 语句定义;如果 settings 返回这样一个字段,改变它的内容会产生影响。

在任何情况下修改 Compact.TupleAsArraySettings.settings.MissingMemberHandling 的值似乎都是不明智的,因为这样做会以可能破坏 backwards compatibility with Json.NET native serialization. As explained in the code comments above, the setting is required to make BackwardCompatible.deserialize 工作的方式修改整个 AppDomain 中 Compact.deserialize 的行为。但为什么会这样呢?由于 Json.NET 的 option 和可区分联合的本机格式看起来像:

{
  "a": {
    "Case": "Some",
    "Fields": [
      3
    ]
  }
}

我们可以猜测,MissingMemberHandling是用来捕获"Case""Fields"是否存在的情况,并从一种算法切换到另一种算法。

如果您确定不需要反序列化 Json.NET 格式的 f# 类型 ,那么您应该可以直接使用 CompactUnionJsonConverter 作为如下:

let settings = JsonSerializerSettings(
    NullValueHandling = NullValueHandling.Ignore,
    Converters = [| CompactUnionJsonConverter(true, true) |]
)
let c = JsonConvert.DeserializeObject<r>(a, settings)
let json2 = JsonConvert.SerializeObject(c, Formatting.Indented, settings)

演示 fiddle here.