I have recently implemented this utility class for going through an instance's member fields recursively and assigning them default values if they are null. Here is the implementation in the NullHandler
class:
public static class NullHandler
{
public static void PopulateNullFields<T>(T obj)
{
var fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach(var field in fields)
{
var defaultValue = field.GetValue(obj);
if(defaultValue == null)
{
defaultValue = GetDefaultValue(field.FieldType);
}
var subFields = field.FieldType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
if(subFields.Count() > 0 && !field.FieldType.IsValueType && field.FieldType != typeof(string))
{
PopulateNullFields(defaultValue);
}
field.SetValue(obj, defaultValue);
}
}
public static object GetDefaultValue(Type t)
{
if(Nullable.GetUnderlyingType(t) != null)
{
t = Nullable.GetUnderlyingType(t);
}
if(t == typeof(string))
{
return "";
}
else if(t == typeof(DateTime))
{
return new DateTime();
}
else if (t.IsValueType)
{
return Activator.CreateInstance(t);
}
else if(t.IsArray)
{
var cinfo = t.GetConstructors().FirstOrDefault(x => x.GetParameters().Count() == 1);
if(cinfo != null)
{
return cinfo.Invoke(new object[] { 0 });
}
else
{
throw new InvalidOperationException("Unable to find array constructor for this array type.");
}
}
else if(t.IsEnum)
{
return Enum.GetValues(t).GetValue(0);
}
else
{
var cinfo = t.GetConstructors().Where(x => x.GetParameters().Count() == 0).FirstOrDefault();
if (cinfo != null)
{
return Activator.CreateInstance(t);
}
else
{
throw new InvalidOperationException("No default constructor with no parameter is found for the type.");
}
}
}
}
The code is currently in use in our XML serialization routine, where we have a requirement for another system that expects an XML file with null values included. The .NET XmlSerializer
class omits null properties in the generated XML unless the properties are decorated with the XmlElement(IsNullable = true)
attribute.
While we would have rather added the attributes to the model class itself, the model class is (a) created from code-generation, and we would lose the changes if we needed to regenerate the model class, and (b) the generated model class is at least ~3000 LOC, making it time-consuming to manually add the needed attributes ourselves.
Here is a sample of how the NullHandler
utility class is in use on our project:
using (var stringWriter = new StringWriter())
using (var xmlStringWriter = XmlWriter.Create(stringWriter, settings))
{
NullHandler.PopulateNullFields(item.Result.Payee);
serializer.Serialize(xmlStringWriter, item.Result);
// ...save XML to database
}
I would like to get feedback if there are areas where the code can be improved. I would specifically like to know how I can improve the reusability of the NullHandler
class by accounting for any possibly missed cases (most especially in the GetDefaultValues
method). Feedback to improve the class's readability would be appreciated as well.