CSharp · Console
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;
}
}
}