考虑包含如下列表值的 F# 记录:
type MyRecord = {
Name: string
SomeList: string list
}
Run Code Online (Sandbox Code Playgroud)
Netwonsoft.Json.JsonConvert
当 JSON 不包含记录的列表值的属性时,使用将 JSON 反序列化为Values
该记录将导致反序列化的记录具有null
列表的值而不是空列表[]
。
那是,
open Newtonsoft.Json
JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "Some name"}""" ) |> printfn "%A"
// Gives: { Name = "Some name"; SomeList = null; }
Run Code Online (Sandbox Code Playgroud)
如何反序列化 usingNetwonsoft.Json
以使列表初始化为空列表?例如:
{ Name = "Some name"; SomeList = []; }
Run Code Online (Sandbox Code Playgroud)
您可以使用自定义合同解析器来执行此操作,如下所示:
type ParameterizedConstructorInitializingContractResolver() =
inherit DefaultContractResolver()
// List is a module not a static class so it's a little inconvenient to access via reflection. Use this wrapper instead.
static member EmptyList<'T>() = List.empty<'T>
override __.CreatePropertyFromConstructorParameter(matchingMemberProperty : JsonProperty, parameterInfo : ParameterInfo) =
let property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo)
if (not (matchingMemberProperty = null) && property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() = typedefof<_ list>) then
let genericMethod = typeof<ParameterizedConstructorInitializingContractResolver>.GetMethod("EmptyList", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.Static)
let concreteMethod = genericMethod.MakeGenericMethod(property.PropertyType.GetGenericArguments())
let defaultValue = concreteMethod.Invoke(null, null)
property.DefaultValue <- defaultValue
property.DefaultValueHandling <- new System.Nullable<DefaultValueHandling>(DefaultValueHandling.Populate)
matchingMemberProperty.DefaultValue <- defaultValue
matchingMemberProperty.DefaultValueHandling <- new System.Nullable<DefaultValueHandling>(DefaultValueHandling.Populate)
property
Run Code Online (Sandbox Code Playgroud)
然后使用它如下:
let settings = JsonSerializerSettings(ContractResolver = new ParameterizedConstructorInitializingContractResolver())
let myrecord1 = JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "Missing SomeList"}""", settings )
let myrecord2 = JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "Populated SomeList", "SomeList" : ["a", "b", "c"]}""", settings )
let myrecord3 = JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "null SomeList", "SomeList" : null}""", settings )
Run Code Online (Sandbox Code Playgroud)
笔记:
合约解析器适用于通过参数化构造函数反序列化的任何对象,其中包括但不限于 f# 记录。如果任何此类对象具有类型T list
为 any 的构造函数参数T
,则该值将在缺失或为 null 时默认为List.empty<T>
。
合约解析器List.empty<T>
对所有反序列化对象重用默认值的相同实例,这在这里很好,因为 f# 列表是不可变的(而且List.empty<T>
似乎是单例)。同样的方法不适用于为可变集合提供默认值作为构造函数参数。
您可能希望缓存合约解析器以获得最佳性能。
构造函数参数必须与相应的属性具有相同的名称(模数大小写)。
演示小提琴在这里。