It's long, repetitive, and hard to follow.
private void Map(object x, JToken json)
{
var objType = x.GetType();
var props = objType.GetProperties().Where(p => p.CanWrite).ToList();
foreach (var prop in props)
{
var type = prop.PropertyType;
var name = prop.Name;
var value = json[name];
var actualName = name;
if (value == null)
{
// try camel cased name
actualName = name.ToCamelCase(Culture);
value = json[actualName];
}
if (value == null)
{
// try lower cased name
actualName = name.ToLower(Culture);
value = json[actualName];
}
if (value == null)
{
// try name with underscores
actualName = name.AddUnderscores();
value = json[actualName];
}
if (value == null)
{
// try name with underscores with lower case
actualName = name.AddUnderscores().ToLower(Culture);
value = json[actualName];
}
if (value == null)
{
// try name with dashes
actualName = name.AddDashes();
value = json[actualName];
}
if (value == null)
{
// try name with dashes with lower case
actualName = name.AddDashes().ToLower(Culture);
value = json[actualName];
}
if (value == null || value.Type == JTokenType.Null)
{
continue;
}
// check for nullable and extract underlying type
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = type.GetGenericArguments()[0];
}
if (type.IsPrimitive)
{
// no primitives can contain quotes so we can safely remove them
// allows converting a json value like {"index": "1"} to an int
var tmpVal = value.AsString().Replace("\"", string.Empty);
prop.SetValue(x, tmpVal.ChangeType(type), null);
}
else if (type.IsEnum)
{
string raw = value.AsString();
var converted = Enum.Parse(type, raw, false);
prop.SetValue(x, converted, null);
}
else if (type == typeof(Uri))
{
string raw = value.AsString();
var uri = new Uri(raw, UriKind.RelativeOrAbsolute);
prop.SetValue(x, uri, null);
}
else if (type == typeof(string))
{
string raw = value.AsString();
prop.SetValue(x, raw, null);
}
else if (type == typeof(DateTime) || type == typeof(DateTimeOffset))
{
DateTime dt;
if (DateFormat.HasValue())
{
var clean = value.AsString();
dt = DateTime.ParseExact(clean, DateFormat, Culture);
}
else if (value.Type == JTokenType.Date)
{
dt = value.Value<DateTime>().ToUniversalTime();
}
else
{
// try parsing instead
dt = value.AsString().ParseJsonDate(Culture);
}
if (type == typeof(DateTime))
prop.SetValue(x, dt, null);
else if (type == typeof(DateTimeOffset))
prop.SetValue(x, (DateTimeOffset)dt, null);
}
else if (type == typeof(Decimal))
{
var dec = Decimal.Parse(value.AsString(), Culture);
prop.SetValue(x, dec, null);
}
else if (type == typeof(Guid))
{
string raw = value.AsString();
var guid = string.IsNullOrEmpty(raw) ? Guid.Empty : new Guid(raw);
prop.SetValue(x, guid, null);
}
else if (type.IsGenericType)
{
var genericTypeDef = type.GetGenericTypeDefinition();
if (genericTypeDef == typeof(List<>))
{
var list = BuildList(type, value.Children());
prop.SetValue(x, list, null);
}
else if (genericTypeDef == typeof(Dictionary<,>))
{
var keyType = type.GetGenericArguments()[0];
// only supports Dict<string, T>()
if (keyType == typeof(string))
{
var dict = BuildDictionary(type, value.Children());
prop.SetValue(x, dict, null);
}
}
else
{
// nested property classes
var item = CreateAndMap(type, json[actualName]);
prop.SetValue(x, item, null);
}
}
else
{
// nested property classes
var item = CreateAndMap(type, json[actualName]);
prop.SetValue(x, item, null);
}
}
}