questions/52276856/ignore-from-custom-contractresolver-on-explicit-null-value-only-for-deserializat

CSharp · Console

/Program.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Data;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Reflection;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;	

using NUnit.Framework;

//https://stackoverflow.com/questions/52276856/ignore-from-custom-contractresolver-on-explicit-null-value-only-for-deserializat

    class CustomContractResolver : DefaultContractResolver
    {
		class NullToDefaultValueProvider : ValueProviderDecorator
		{
			readonly object defaultValue;
		
			public NullToDefaultValueProvider(IValueProvider baseProvider, object defaultValue) : base(baseProvider)
			{
				this.defaultValue = defaultValue;
			}
			
			public override void SetValue(object target, object value)
			{
				base.SetValue(target, value ?? defaultValue);
			}
		}
	
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            JsonProperty property = base.CreateProperty(member, memberSerialization);
            if (property != null && property.PropertyType.IsValueType && Nullable.GetUnderlyingType(property.PropertyType) == null && property.Writable)
            {
                var defaultValue = property.DefaultValue ?? Activator.CreateInstance(property.PropertyType);

                // When a null value is encountered in the JSON we want to set a default value in the class.
                property.PropertyType = typeof(Nullable<>).MakeGenericType(new[] { property.PropertyType });
                property.ValueProvider = new NullToDefaultValueProvider(property.ValueProvider, defaultValue);

                // Remember that the underlying property is actually not nullable so GetValue() will never return null.
                // Thus the below just overrides JsonSerializerSettings.NullValueHandling to force the value to be set
                // (to the default) even when null is encountered.
                property.NullValueHandling = NullValueHandling.Include;
            }
            return property;
        }

        // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
        // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
        // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
        // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
        // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
        static CustomContractResolver instance;

        // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
        static CustomContractResolver() { instance = new CustomContractResolver(); }

        public static CustomContractResolver Instance { get { return instance; } }

    }

    public abstract class ValueProviderDecorator : IValueProvider
    {
        readonly IValueProvider baseProvider;

        public ValueProviderDecorator(IValueProvider baseProvider)
        {
            if (baseProvider == null)
                throw new ArgumentNullException();
            this.baseProvider = baseProvider;
        }

        public virtual object GetValue(object target) { return baseProvider.GetValue(target); }

        public virtual void SetValue(object target, object value) { baseProvider.SetValue(target, value); }
    }
	
	public enum SomeEnum
	{
		Zero,
		One,
	}

    public class NoConfigModel
    {
		public NoConfigModel()
		{
			PropertyB = 101;
			PropertyC = true;
			PropertyD = DateTime.Now;
			SomeEnum = SomeEnum.One;
		}
		
        public string PropertyA { get; set; }

        public int PropertyB { get; set; }
        public bool PropertyC { get; set; }
		public DateTime PropertyD { get; set; }
		public SomeEnum SomeEnum { get; set; }

		public DateTime? PropertyNullable { get; set; }
	}

    class TestClass
    {
        public static void Test()
        {
			var jObj = JObject.FromObject(new NoConfigModel());
			foreach (var property in jObj.Properties())
				property.Value = null;
			
			var jsonString = jObj.ToString();

			Console.WriteLine("\nInput JSON: ");
			Console.WriteLine(jsonString);
			
			var settings = new JsonSerializerSettings
			{
				ContractResolver = CustomContractResolver.Instance,
				NullValueHandling = NullValueHandling.Ignore,
				DefaultValueHandling = DefaultValueHandling.Populate,
				Converters = { new StringEnumConverter() },
			};
			
			var jsonObject = JsonConvert.DeserializeObject<NoConfigModel>(jsonString, settings);
			
			Console.WriteLine("\nDeserialized object using NullValueHandling.Ignore and CustomContractResolver; re-serialized with default settings: ");
			Console.WriteLine(JsonConvert.SerializeObject(jsonObject, Formatting.Indented));
			Console.WriteLine("\nDeserialized object using NullValueHandling.Ignore and CustomContractResolver; re-serialized using NullValueHandling.Ignore and CustomContractResolver: ");
			Console.WriteLine(JsonConvert.SerializeObject(jsonObject, Formatting.Indented, settings));
			
			Assert.IsTrue(jsonObject.PropertyB == default(int));
			Assert.IsTrue(jsonObject.PropertyC == default(bool));
			Assert.IsTrue(jsonObject.PropertyD == default(DateTime));
			Assert.IsTrue(jsonObject.SomeEnum == default(SomeEnum));
			
			Assert.NotNull(jsonObject);
			
			deserialize_test();
			
		}
		
		public static void deserialize_test()
		{
			var settings = new JsonSerializerSettings { ContractResolver = CustomContractResolver.Instance };

			var jsonString = "{\"PropertyA\":\"Test\",\"PropertyB\":null}";
			var jsonObject = JsonConvert.DeserializeObject<NoConfigModel>(jsonString, settings);
			Assert.NotNull(jsonObject);
		}
	}

	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Environment version: " + Environment.Version);
			Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
			Console.WriteLine();

			try
			{
				TestClass.Test();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Failed with unhandled exception: ");
				Console.WriteLine(ex);
				throw;
			}
		}
	}

;