这是以下问题:反序列化问题,json.net,在 F# 中。
我正在使用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
它仍返回 json.net 错误:
在类型为"r"的对象上找不到成员"b"。路径"b",第 1 行,位置 13。
有没有办法做到这一点,还是FSharpLu.Json的问题?
这是小提琴: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
是一个静态成员,如代码所示:
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# 所述,为了好玩和获利:将函数附加到类型,settings
实际上(在 C# 术语中(是一个静态属性,每次调用时都会返回一个新的JsonSerializerSettings
实例。 为了测试这一点,我们可以做:
printfn "%b" (Object.ReferenceEquals(Compact.TupleAsArraySettings.settings, Compact.TupleAsArraySettings.settings)) // prints "false"
哪个打印"假"。 因此,改变返回值对Compact
的行为没有影响。 c# 意义上的静态字段将由static let
语句定义;如果settings
返回这样的字段,则更改其内容将产生影响。
在任何情况下,修改Compact.TupleAsArraySettings.settings.MissingMemberHandling
的值似乎都是不明智的,因为这样做会修改整个 AppDomain 中Compact.deserialize
的行为,从而可能破坏与本机序列化 Json.NET 向后兼容性。 如上面的代码注释中所述,需要该设置才能使BackwardCompatible.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)
演示小提琴在这里。