Json.NET根据属性类型使属性成为必需

时间:2018-01-18 18:13:20

标签: c# serialization asp.net-core f# json.net

我正在努力使用.Net核心中的自定义json序列化,我正在尝试默认所需的所有属性,除非属性具有特定类型。这是我想要实现的一个例子:

让我们确定我有以下类型: F#:

type FooType = {
   id: int
   name: string 
   optional: int option 
}

你可以考虑下面的代码与C#中的跟随类似:

class FooType =
{
   int Id {get;set;};
   string Name {get;set;};
   Nullable<int> Optional {get;set;};
}

我想要做的是返回错误,如果json对象中缺少Id或Name属性,但如果缺少Optional,则反序列化而不会出错(因此基本上根据需要将属性设置为不需要)。我可以使用此示例中的RequireObjectPropertiesContractResolver标记所有属性:https://stackoverflow.com/a/29660550但遗憾的是我无法构建更具动态性的内容。

我还有我希望添加到序列化的可选类型的默认转换器。它不是这个特定问题的一部分,但是如果你知道如何标记属性是否需要,并且在一个地方使用自定义转换器比它更大。

1 个答案:

答案 0 :(得分:4)

您可以将Json.NET require all properties on deserialization的合同解析程序与Reflection to find out if property is of option type的答案中的逻辑p.s.w.g结合起来,以标记除了根据需要可选的成员之外的所有成员:

type RequireObjectPropertiesContractResolver() =
    inherit DefaultContractResolver()

    override this.CreateObjectContract(objectType : Type) = 
        let contract = base.CreateObjectContract(objectType)
        contract.ItemRequired <- new System.Nullable<Required>(Required.Always);
        contract

    override this.CreateProperty(memberInfo : MemberInfo, memberSerialization : MemberSerialization) =
        let property = base.CreateProperty(memberInfo, memberSerialization);
        // https://stackoverflow.com/questions/20696262/reflection-to-find-out-if-property-is-of-option-type
        let isOption = property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() = typedefof<Option<_>>
        if isOption then (
            property.Required <- Required.Default        
            property.NullValueHandling <- new System.Nullable<NullValueHandling>(NullValueHandling.Ignore)
        )
        property

然后,按如下方式反序列化:

let settings = new JsonSerializerSettings()
settings.ContractResolver <- new RequireObjectPropertiesContractResolver()
let obj = JsonConvert.DeserializeObject<FooType>(inputJson, settings)

注意:

  • 我还添加了NullValueHandling.Ignore,以便不会将没有值的可选成员序列化。

  • 您可能需要cache the contract resolver for best performance

  • Option<'T>Nullable<'T>不同。我检查了typedefof<Option<_>>,但如果您愿意,还可以添加typedefof<System.Nullable<_>>支票:

    let isOption = property.PropertyType.IsGenericType && (property.PropertyType.GetGenericTypeDefinition() = typedefof<Option<_>> || property.PropertyType.GetGenericTypeDefinition() = typedefof<System.Nullable<_>>)
    

示例fiddle,它表明字符串{"id":101,"name":"John"}可以反序列化,但字符串{"id":101}不能反序列化。

相关问题