public async Task EvaluateDynamicProperty()

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;
        }