HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Parsing an ExpandoObject into a typed class using reflection

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
reflectionintoexpandoobjecttypedparsingusingclass

Problem

I've spent some time writing a library to parse JSON into a statically typed object. Am I following most of the standard coding guidelines? Any thoughts on what I could do better, or any edge cases that are missing and aren't taken care of?

```
private static T ParseDynamic(ExpandoObject input)
{
//Parse when given an ExpandoObject
T output = default(T);
var dict = input as IDictionary;
ParseDictionary(dict, out output);
return output;
}
protected static void ParseDictionary(IDictionary Dict, out object Target, Type explicitType)
{
if(Dict == null)
{
throw new InvalidOperationException("Dictionary was null, cannot parse a null dictionary");
}
if (explicitType.IsArray)
{
var length = Dict.Keys.Count();
Target = (Array)Activator.CreateInstance(explicitType, new object[] { length });
}
else
{
Target = Activator.CreateInstance(explicitType);
}
foreach (var property in Target.GetType().GetProperties())
{
var propertyName = property.Name;
if (Dict.ContainsKey(propertyName) && Dict[propertyName] != null)
{
var val = Dict[propertyName];
var propertyVal = explicitType.GetProperty(propertyName);
var expectedType = property.PropertyType;
var valType = val.GetType();
if(valType == expectedType)
{
//Hurray, we matched!
propertyVal.SetValue(Target, val);
}
else if (valType != expectedType && val is IConvertible)
{
Type safeType = Nullable.GetUnderlyingType(expectedType) ?? expectedType;
//Special Case - INT64 to DATETIME Conversion (UNIX Time)
if((valType == typeof(long) || valType == typeof(long?))
&& (safeType == typeof(DateTime) || safeType == typeof(DateTime?)))
{
var longValue = (long)Convert.ChangeType(val, typeof

Solution

protected static void ParseDictionary(IDictionary Dict, out object Target, Type explicitType)

I would say this method declaration is a big code smell. Having a void method with an out parameter just isn't right.

Why don't you let the method return an object ? There is just no reason.

Although it had been already mentioned by @akw5013 in his answer, based on the NET naming guidelines methodparameters should be named using camelCase casing.

If you choose a style for yourself, which is a valid decision, you should stick to this style. At this declaration you are mixing styles which is always a bad idea.

if (Dict.ContainsKey(propertyName) && Dict[propertyName] != null)
{
    var val = Dict[propertyName];
    var propertyVal = explicitType.GetProperty(propertyName);


This will be "incredible" slow. Internally this calls 3 times the FindEntry(key) method through the following methods

public bool ContainsKey(TKey key) {
    return FindEntry(key) >= 0;
}  

public TValue this[TKey key] {
    get {
        int i = FindEntry(key);
        if (i >= 0) return entries[i].value;
        ThrowHelper.ThrowKeyNotFoundException();
        return default(TValue);
    }
    set {
        Insert(key, value, false);
    }
}


if you would use Dictionary.TryGetValue() this would result in only one call to

public bool TryGetValue(TKey key, out TValue value) {
    int i = FindEntry(key);
    if (i >= 0) {
        value = entries[i].value;
        return true;
    }
    value = default(TValue);
    return false;
}


but fortunately this problem is easy to fix like so

object val;
    var propertyName = property.Name;
    if (Dict.TryGetValue(propertyName, out val) && val != null)
    {
        var propertyVal = explicitType.GetProperty(propertyName);


that had been easy, hadn't it ?

if(valType == expectedType)
{
    //Hurray, we matched!
    propertyVal.SetValue(Target, val);
}
else if (valType != expectedType && val is IConvertible)


if the execution comes to the else if there is a 100% chance that valType != expectedType will be true.

else if (valType != expectedType && val is IConvertible)
{
    Type safeType = Nullable.GetUnderlyingType(expectedType) ?? expectedType;
    //Special Case - INT64 to DATETIME Conversion (UNIX Time)
    if ((valType == typeof(long) || valType == typeof(long?))
        && (safeType == typeof(DateTime) || safeType == typeof(DateTime?)))
    {
        var longValue = (long)Convert.ChangeType(val, typeof(long));
        var dateValue = UNIX_EPOCH.AddSeconds(longValue);
        val = dateValue;
    }
    //Convert if possible
    var explicitVal = (val == null ? null : Convert.ChangeType(val, safeType));
    propertyVal.SetValue(Target, explicitVal, null);

}


the use of dateValue can just be skipped. Assigning the return value of UNIX_EPOCH.AddSeconds() to val is enough.

The tenary expression would IMO be better just an if..else like so

if (val == null)
{
    propertyVal.SetValue(Target, null, null);
}
else 
{
    propertyVal.SetValue(Target, Convert.ChangeType(val, safeType), null);
}


else if (val is IDictionary)
{
    //Parse non-simple object
    var propType = propertyVal.PropertyType;
    object explicitVal = Activator.CreateInstance(propType);
    ParseDictionary(val as IDictionary, out explicitVal, propType);
    propertyVal.SetValue(Target, explicitVal);
}


Here you are creating an instance of propType which is overwritten inside the recursively called ParseDictionary() method. Just skip it.

var listType = typeof(List<>);
var typedList = listType.MakeGenericType(elementType);


the var listType is only used once at the next line of code. You can compact this like so

var typedList = typeof(List<>).MakeGenericType(elementType);


and if you want you can just keep your pattern of typeType so typedList will become listType.

The creation of the List of this else if branch should be extracted to a separate method like so

private static IList ParseAsList(object val, Type expectedType, PropertyInfo property)
{
    Type elementType = null;
    if (expectedType.IsArray) //Array type is explicitly included with GetElementType
    {
        elementType = expectedType.GetElementType();
    }
    else if (expectedType.IsGenericType) //Get List type by inspecting generic argument
    {
        elementType = expectedType.GetGenericArguments()[0];
    }

    var listType = typeof(List<>).MakeGenericType(elementType);
    var explicitList = (IList)Activator.CreateInstance(listType);

    foreach (var element in val as IList)
    {
        object explicitElement = ParseDictionary(element as IDictionary, elementType);
        explicitList.Add(explicitElement);
    }

    return explicitList;
}


which results in the branch beeing

```
else if (val is IList)
{
//Parse list/enumeration/array

if (!(expectedType.IsArray || expectedType.IsGe

Code Snippets

if (Dict.ContainsKey(propertyName) && Dict[propertyName] != null)
{
    var val = Dict[propertyName];
    var propertyVal = explicitType.GetProperty(propertyName);
public bool ContainsKey(TKey key) {
    return FindEntry(key) >= 0;
}  

public TValue this[TKey key] {
    get {
        int i = FindEntry(key);
        if (i >= 0) return entries[i].value;
        ThrowHelper.ThrowKeyNotFoundException();
        return default(TValue);
    }
    set {
        Insert(key, value, false);
    }
}
public bool TryGetValue(TKey key, out TValue value) {
    int i = FindEntry(key);
    if (i >= 0) {
        value = entries[i].value;
        return true;
    }
    value = default(TValue);
    return false;
}
object val;
    var propertyName = property.Name;
    if (Dict.TryGetValue(propertyName, out val) && val != null)
    {
        var propertyVal = explicitType.GetProperty(propertyName);
if(valType == expectedType)
{
    //Hurray, we matched!
    propertyVal.SetValue(Target, val);
}
else if (valType != expectedType && val is IConvertible)

Context

StackExchange Code Review Q#106669, answer score: 5

Revisions (0)

No revisions yet.