in Forge.TreeWalker/src/TreeWalkerSession.cs [963:1095]
public async Task<object> EvaluateDynamicProperty(dynamic schemaObj, Type knownType)
{
try
{
if (schemaObj == null)
{
return null;
}
if (schemaObj is string && schemaObj.StartsWith(RoslynLeadingText))
{
// Case when schema property is a Roslyn expression.
// Evaluate it as either the knownType if it exists, the <type> embeded in the RoslynRegex, or an object by default, in that order.
Match result = RoslynRegex.Match(schemaObj);
if (result.Success)
{
string typeStr = string.IsNullOrWhiteSpace(result.Groups[2].Value) ? "Object" : result.Groups[2].Value;
Type type = knownType ?? Type.GetType("System." + typeStr);
MethodInfo method = typeof(ExpressionExecutor).GetMethod("Execute");
MethodInfo genericMethod = method.MakeGenericMethod(type);
string expression = schemaObj.Substring(result.Groups[0].Value.Length);
var res = await (dynamic) genericMethod.Invoke(this.expressionExecutor, new object[] { expression });
return res;
}
}
else if (schemaObj is string)
{
foreach (var kvp in this.Parameters.ExternalExecutors)
{
string regexString = kvp.Key;
Func<string, CancellationToken, Task<object>> externalExecutor = kvp.Value;
if (schemaObj.StartsWith(regexString))
{
// Case when schema property matches an external executor.
// Evaluate it and return the result as the knownType if it exists or as a string otherwise.
var result = await externalExecutor(schemaObj.Substring(regexString.Length), this.walkTreeCts.Token).ConfigureAwait(false);
return knownType != null ? Convert.ChangeType(result, knownType) : result;
}
}
if (knownType?.IsEnum ?? false)
{
// Case when schema property is an Enum type.
return Enum.Parse(knownType, schemaObj);
}
return schemaObj;
}
else if (schemaObj is JObject)
{
// Case when schema object has properties (i.e. is an object or a dictionary).
// Instantiate and use the knownType if given, then evaluate each property using recursion.
dynamic knownObj = Activator.CreateInstance(knownType ?? typeof(object));
IDictionary<string, dynamic> propertyValues = schemaObj.ToObject<IDictionary<string, dynamic>>();
foreach (string key in new List<string>(propertyValues.Keys))
{
if (knownObj is IDictionary && knownType.IsGenericType && knownType.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>)))
{
// Case when schema object is explicitly defined as an IDictionary knownType.
var prop = knownType.GetProperty("Item");
prop.SetValue(knownObj, await this.EvaluateDynamicProperty(propertyValues[key], prop.PropertyType).ConfigureAwait(false), new[] { key });
}
else if (knownType != null && knownType != typeof(object))
{
// Case when schema object is a knownType object.
var prop = knownType.GetProperty(key);
prop.SetValue(knownObj, await this.EvaluateDynamicProperty(propertyValues[key], prop.PropertyType).ConfigureAwait(false));
}
else
{
// Case when schema object is a dynamic JObject object.
propertyValues[key] = await this.EvaluateDynamicProperty(propertyValues[key], null).ConfigureAwait(false);
}
}
return knownType != null && knownType != typeof(object) ? knownObj : (dynamic)JObject.FromObject(propertyValues);
}
else if (schemaObj is JArray)
{
// Case when schema object is an array.
// Create an array with the knownType if given, then use recursion to evaluate each index.
dynamic knownObj = Activator.CreateInstance(knownType ?? typeof(object[]), schemaObj.Count);
Type knownTypeElement = knownType != null && knownType != typeof(object) ? knownType.GetElementType() : null;
for (int i = 0; i < schemaObj.Count; i++)
{
knownObj.SetValue(await this.EvaluateDynamicProperty(schemaObj[i].ToObject<object>(), knownTypeElement).ConfigureAwait(false), i);
}
return knownObj;
}
else if (schemaObj is JValue)
{
// Case when schema object is a value type.
// Return the value as the knownType if given.
return knownType != null ? Convert.ChangeType(schemaObj.Value, knownType) : schemaObj.Value;
}
else
{
// Case when schema object is a value type.
// Return the value as an Enum if knownType is an Enum.
// Return the value as the knownType if given.
if (knownType == null)
{
return schemaObj;
}
else if (knownType.IsEnum)
{
return Enum.Parse(knownType, schemaObj.ToString());
}
else
{
return Convert.ChangeType(schemaObj, knownType);
}
}
}
catch (OperationCanceledException)
{
throw; // rethrow on cancelled.
}
catch (Exception e)
{
throw new EvaluateDynamicPropertyException(
string.Format("EvaluateDynamicProperty failed to parse schemaObj: {0}, knownType: {1}.", schemaObj?.ToString(), knownType),
e);
}
return null;
}