I guess you are in for some long hours... looks to me like you have to develop an ORM, I was faced with the same once where I had to make an ORM for ATM machines.
This code will not work for you as it assumes an IDataReader however you are in need of the property mapping using reflection and this code is in there.
Let me know if this get's you going as it has some optimisation in it for reusing reflected types etc.
I think you need to test for a property being a Class and create it using the activator and use an efficient cast using
if (property.PropertyType.BaseType == typeof(Enum))
{
property.SetValue(obj, (int)value);
}
else if (property.PropertyType.BaseType == typeof(Guid))
{
property.SetValue(obj, Guid.Parse(value.ToString().ToUpper()));
}
else
{
property.SetValue(obj, Convert.ChangeType(value, property.PropertyType));
}
Ido not know how my callses you need to support or if it is a fixed amount, you might just be after making a static T Parse(this T target, string json) method
where you fragment the json string based on { and } brackets to get properties and [ and ] to get Arrays.
Here is the code for that, I used it for a VCARD Json parser I hade to make some time ago
/// <summary>
/// Splits the specified string in sections of open en closing characters.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="open">The opening char indicating where to start to read .</param>
/// <param name="close">The close char, indicating the part where should stop reading.</param>
/// <returns>IReadOnlyList<System.String>.</returns>
/// <exception cref="System.ArgumentNullException">text</exception>
/// <exception cref="ArgumentNullException">Will throw an exception if the string that needs to be split is null or empty</exception>
public static IReadOnlyList<string> Split(this string text, char open, char close)
{
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
var counted = 0;
var result = new List<string>();
var sb = new StringBuilder();
foreach (char c in text)
{
if (c == open)
{
if (counted != 0)
sb.Append(c);
counted++;
continue;
}
if (c == close)
{
counted--;
if (counted != 0)
sb.Append(c);
continue;
}
if (counted > 0)
{
sb.Append(c);
}
else if (counted == 0 && sb.Length > 0)
{
result.Add(sb.ToString());
sb.Clear();
}
}
return result;
}
Here is the full mapper I had to make where you see the mentioned reflection
class Mapper
{
ConcurrentDictionary<Type, PropertyInfo[]> _properties = new ConcurrentDictionary<Type, PropertyInfo[]>();
ConcurrentDictionary<string, List<string>> _fieldNames = new ConcurrentDictionary<string, List<string>>();
/// <summary>
/// Maps the specified reader to a given class. the reader must contain all properties of the type provided.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader">The reader.</param>
/// <returns></returns>
public IEnumerable<T> Map<T>(SqlDataReader reader)
{
var result = new List<T>();
if (!reader.HasRows)
return result;
var type = typeof(T);
if (!_properties.TryGetValue(type, out PropertyInfo[] prop))
{
prop = type.GetProperties();
_properties.TryAdd(type, prop);
}
if (!_fieldNames.TryGetValue(type.Name, out List<string> fieldNames))
{
var names = new List<string>(reader.FieldCount);
for (int i = 0; i < reader.FieldCount; i++)
{
names.Add(reader.GetName(i));
}
fieldNames = names;
_fieldNames.TryAdd(type.Name, fieldNames);
}
while (reader.Read())
{
var obj = Activator.CreateInstance<T>();
foreach (var property in prop)
{
if (fieldNames.Contains(property.Name))
{
var value = reader[property.Name];
if (value == DBNull.Value)
continue;
if (property.PropertyType.BaseType == typeof(Enum))
{
property.SetValue(obj, (int)value);
}
else if (property.PropertyType.BaseType == typeof(Guid))
{
property.SetValue(obj, Guid.Parse(value.ToString().ToUpper()));
}
else
{
property.SetValue(obj, Convert.ChangeType(value, property.PropertyType));
}
}
}
result.Add(obj);
}
return result;
}
public IEnumerable<T> Map<T,Y>(SqlDataReader reader,Y owner)
{
var result = new List<T>();
if (!reader.HasRows)
return result;
var type = typeof(T);
if (!_properties.TryGetValue(type, out PropertyInfo[] prop))
{
prop = type.GetProperties();
_properties.TryAdd(type, prop);
}
if (!_fieldNames.TryGetValue(type.Name, out List<string> fieldNames))
{
var names = new List<string>(reader.FieldCount);
for (int i = 0; i < reader.FieldCount; i++)
{
names.Add(reader.GetName(i));
}
fieldNames = names;
_fieldNames.TryAdd(type.Name, fieldNames);
}
while (reader.Read())
{
var obj = Activator.CreateInstance<T>();
foreach (var property in prop)
{
if (property.PropertyType == typeof(Y))
{
property.SetValue(obj, owner);
continue;
}
if (fieldNames.Contains(property.Name))
{
var value = reader[property.Name];
if (value == DBNull.Value)
continue;
if (property.PropertyType.BaseType == typeof(Enum))
{
property.SetValue(obj, (int)value);
}
else
{
property.SetValue(obj, Convert.ChangeType(value, property.PropertyType));
}
}
}
result.Add(obj);
}
return result;
}
/// <summary>
/// Maps the specified reader to a given class. the reader must contain all properties of the type provided.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader">The reader.</param>
/// <returns></returns>
public T MapOne<T>(SqlDataReader reader)
{
if (!reader.HasRows)
return default;
var type = typeof(T);
if (!_properties.TryGetValue(type, out PropertyInfo[] prop))
{
prop = type.GetProperties();
_properties.TryAdd(type, prop);
}
if (!_fieldNames.TryGetValue(type.Name, out List<string> fieldNames))
{
var names = new List<string>(reader.FieldCount);
for (int i = 0; i < reader.FieldCount; i++)
{
names.Add(reader.GetName(i));
}
fieldNames = names;
_fieldNames.TryAdd(type.Name, fieldNames);
}
if (reader.Read())
{
var obj = Activator.CreateInstance<T>();
foreach (var property in prop)
{
if (fieldNames.Contains(property.Name))
property.SetValue(obj, reader[property.Name]);
}
return obj;
}
else
{
return default;
}
}
/// <summary>
/// Maps the specified reader to a given class. the reader must contain all properties of the type provided.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader">The reader.</param>
/// <returns></returns>
public IEnumerable<T> Map<T>(SqlDataReader reader, object[] args)
{
var result = new List<T>();
if (!reader.HasRows)
return result;
var type = typeof(T);
if (!_properties.TryGetValue(type, out PropertyInfo[] prop))
{
prop = type.GetProperties();
_properties.TryAdd(type, prop);
}
if (!_fieldNames.TryGetValue(type.Name, out List<string> fieldNames))
{
var names = new List<string>(reader.FieldCount);
for (int i = 0; i < reader.FieldCount; i++)
{
names.Add(reader.GetName(i));
}
fieldNames = names;
_fieldNames.TryAdd(type.Name, fieldNames);
}
while (reader.Read())
{
var obj = (T)Activator.CreateInstance(type, args);
foreach (var property in prop)
{
if (fieldNames.Contains(property.Name))
property.SetValue(obj, reader[property.Name]);
}
result.Add(obj);
}
return result;
}
}