Json.NET make property required based on property type

蹲街弑〆低调 提交于 2019-12-10 17:26:10

问题


I'm struggling with custom json serialization in .Net core, I'm trying to make all properties required by default except if property has specific type. Here is an example of what I'm trying to achieve:

Let's assument that I have following type: F#:

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

you can think about below code as similar to following in C#:

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

What I'm trying to do is to return error if Id or Name property is missing in json object but deserialize without error if Optional is missing (so basically to set Property as required or not based on it's type). I'm able to mark all properties as required by using RequireObjectPropertiesContractResolver from this sample: https://stackoverflow.com/a/29660550 but unfortunately I wasn't able to build something more dynamic.

I have also default converter for Optional types I would like to add to serialization. It's not part of this one specific question but if you have an idea how to mark property to be required or not and to use custom Converter in one place than it would be even greater.


回答1:


You can combine the contract resolver from Json.NET require all properties on deserialization with the logic from the answer to Reflection to find out if property is of option type by p.s.w.g to mark all members except those that are optional as required:

type RequireObjectPropertiesContractResolver() =
    inherit DefaultContractResolver()

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

    override __.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

Then, deserialize as follows:

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

Notes:

  • I also added NullValueHandling.Ignore so that optional members with no value would not get serialized.

  • You may want to cache the contract resolver for best performance.

  • Option<'T> is not the same as Nullable<'T>. I checked for typedefof<Option<_>> but you could add a check for typedefof<System.Nullable<_>> as well if you want:

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

Sample fiddle, which demonstrates that the string {"id":101,"name":"John"} can be deserialized, but the string {"id":101} cannot.



来源:https://stackoverflow.com/questions/48327799/json-net-make-property-required-based-on-property-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!