问题
This is a following to: deserialization issue, with json.net, in F#.
I am deserializing some JSON that has an extra, unbound property using FSharpLu.Json. Here is the code:
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
Despite setting MissingMemberHandling.Ignore
it returns a json.net error:
Could not find member 'b' on object of type 'r'. Path 'b', line 1, position 13.
Is there a way to make this work, or is it an issue with FSharpLu.Json?
Here is the fiddle: https://dotnetfiddle.net/OsVv1M
as a side note, there is another deserializer in FSharpLu.Json and I can get that code to work with it:
FSharpLu.Json.Default.Internal.DefaultSettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Default.deserialize<r> a
will work, but that deserializer doesn't handle discriminated unions... so I need to get the compact one to work.
When looking into the source of FSharpLu.Json, I found this:
/// 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) |]
)
so they are explicitly setting the MissingMemberHandling to Error; maybe the solution is to instantiate the deserializer, change the setting and then use it.
回答1:
The serializer setting you are trying to mutate, Compact.TupleAsArraySettings.settings
, is a static member, as is shown in the code:
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) |] )
Since a member
is actually a member function (i.e. a method) as explained F# for fun and profit: Attaching functions to types, settings
is actually (in c# terminology) a static property returning a new instance of JsonSerializerSettings
each time it is invoked. To test this, we can do:
printfn "%b" (Object.ReferenceEquals(Compact.TupleAsArraySettings.settings, Compact.TupleAsArraySettings.settings)) // prints "false"
Which prints "false". Thus mutating the returned value has no effect on the behavior of Compact
. A static field in the c# sense would be defined by a static let
statement; if settings
returned such a field, mutating its contents would have had an effect.
In any event modifying the value of Compact.TupleAsArraySettings.settings.MissingMemberHandling
seems unwise as doing so would modify the behavior of of Compact.deserialize
throughout your entire AppDomain in a way that might break backwards compatibility with Json.NET native serialization. As explained in the code comments above, the setting is required to make BackwardCompatible.deserialize work. But why might that be? Since Json.NET's native format for option
and discriminated unions looks like:
{
"a": {
"Case": "Some",
"Fields": [
3
]
}
}
We can guess that MissingMemberHandling
is used to trap situations where "Case"
and "Fields"
are found or not, and switch from one algorithm to the other.
If you are certain you do not need to deserialize f# types in Json.NET format, it seems you should be able to use CompactUnionJsonConverter directly as follows:
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)
Demo fiddle here.
来源:https://stackoverflow.com/questions/62364229/ignoremissingmember-setting-doesnt-seem-to-work-with-fsharplu-json-deserializer